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@hotmail.com>
This commit is contained in:
softworkz
2025-05-15 23:07:54 +02:00
parent e2f39671ae
commit 517a805565
9 changed files with 785 additions and 3 deletions

5
configure vendored
View File

@ -523,6 +523,7 @@ Developer options (useful when working on FFmpeg itself):
--enable-macos-kperf enable macOS kperf (private) API
--disable-large-tests disable tests that use a large amount of memory
--disable-ptx-compression don't compress CUDA PTX code even when possible
--disable-resource-compression don't compress resources even when possible
--disable-version-tracking don't include the git/release version in the build
NOTE: Object files are built at the place where configure is launched.
@ -2123,6 +2124,7 @@ CONFIG_LIST="
ossfuzz
pic
ptx_compression
resource_compression
thumb
valgrind_backtrace
xmm_clobber_test
@ -4179,6 +4181,7 @@ enable iamf
enable large_tests
enable optimizations
enable ptx_compression
enable resource_compression
enable runtime_cpudetect
enable safe_bitstream_reader
enable static
@ -6904,6 +6907,8 @@ EOF
enabled zlib_gzip && enabled gzip || disable ptx_compression
enabled zlib_gzip && enabled gzip || disable resource_compression
# On some systems dynamic loading requires no extra linker flags
check_lib libdl dlfcn.h "dlopen dlsym" || check_lib libdl dlfcn.h "dlopen dlsym" -ldl

View File

@ -139,6 +139,44 @@ else
$(BIN2C) $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) $@ $(subst .,_,$(basename $(notdir $@)))
endif
# 1) Preprocess CSS to a minified version
%.css.min: %.css
# Must start with a tab in the real Makefile
sed 's!/\\*.*\\*/!!g' $< \
| tr '\n' ' ' \
| tr -s ' ' \
| sed 's/^ //; s/ $$//' \
> $@
ifdef CONFIG_RESOURCE_COMPRESSION
# 2) Gzip the minified CSS
%.css.min.gz: %.css.min
$(M)gzip -nc9 $< > $@
# 3) Convert the gzipped CSS to a .c array
%.css.c: %.css.min.gz $(BIN2CEXE)
$(BIN2C) $< $@ $(subst .,_,$(basename $(notdir $@)))
# 4) Gzip the HTML file (no minification needed)
%.html.gz: %.html
$(M)gzip -nc9 $< > $@
# 5) Convert the gzipped HTML to a .c array
%.html.c: %.html.gz $(BIN2CEXE)
$(BIN2C) $< $@ $(subst .,_,$(basename $(notdir $@)))
else # NO COMPRESSION
# 2) Convert the minified CSS to a .c array
%.css.c: %.css.min $(BIN2CEXE)
$(BIN2C) $< $@ $(subst .,_,$(basename $(notdir $@)))
# 3) Convert the plain HTML to a .c array
%.html.c: %.html $(BIN2CEXE)
$(BIN2C) $< $@ $(subst .,_,$(basename $(notdir $@)))
endif
clean::
$(RM) $(BIN2CEXE) $(CLEANSUFFIXES:%=ffbuild/%)
@ -191,9 +229,10 @@ SKIPHEADERS += $(ARCH_HEADERS:%=$(ARCH)/%) $(SKIPHEADERS-)
SKIPHEADERS := $(SKIPHEADERS:%=$(SUBDIR)%)
HOBJS = $(filter-out $(SKIPHEADERS:.h=.h.o),$(ALLHEADERS:.h=.h.o))
PTXOBJS = $(filter %.ptx.o,$(OBJS))
RESOURCEOBJS = $(filter %.css.o %.html.o,$(OBJS))
$(HOBJS): CCFLAGS += $(CFLAGS_HEADERS)
checkheaders: $(HOBJS)
.SECONDARY: $(HOBJS:.o=.c) $(PTXOBJS:.o=.c) $(PTXOBJS:.o=.gz) $(PTXOBJS:.o=)
.SECONDARY: $(HOBJS:.o=.c) $(PTXOBJS:.o=.c) $(PTXOBJS:.o=.gz) $(PTXOBJS:.o=) $(RESOURCEOBJS:.o=.c) $(RESOURCEOBJS:%.css.o=%.css.min) $(RESOURCEOBJS:%.css.o=%.css.min.gz) $(RESOURCEOBJS:%.html.o=%.html.gz) $(RESOURCEOBJS:.o=)
alltools: $(TOOLS)
@ -214,7 +253,7 @@ $(TOOLOBJS): | tools
OUTDIRS := $(OUTDIRS) $(dir $(OBJS) $(HOBJS) $(HOSTOBJS) $(SLIBOBJS) $(SHLIBOBJS) $(STLIBOBJS) $(TESTOBJS))
CLEANSUFFIXES = *.d *.gcda *.gcno *.h.c *.ho *.map *.o *.objs *.pc *.ptx *.ptx.gz *.ptx.c *.ver *.version *$(DEFAULT_X86ASMD).asm *~ *.ilk *.pdb
CLEANSUFFIXES = *.d *.gcda *.gcno *.h.c *.ho *.map *.o *.objs *.pc *.ptx *.ptx.gz *.ptx.c *.ver *.version *.html.gz *.html.c *.css.gz *.css.c *$(DEFAULT_X86ASMD).asm *~ *.ilk *.pdb
LIBSUFFIXES = *.a *.lib *.so *.so.* *.dylib *.dll *.def *.dll.a
define RULES

View File

@ -42,7 +42,7 @@ ifdef HAVE_GNU_WINDRES
OBJS-$(1) += fftools/fftoolsres.o
endif
$(1)$(PROGSSUF)_g$(EXESUF): $$(OBJS-$(1))
$$(OBJS-$(1)): | fftools fftools/textformat
$$(OBJS-$(1)): | fftools fftools/textformat fftools/resources
$$(OBJS-$(1)): CFLAGS += $(CFLAGS-$(1))
$(1)$(PROGSSUF)_g$(EXESUF): LDFLAGS += $(LDFLAGS-$(1))
$(1)$(PROGSSUF)_g$(EXESUF): FF_EXTRALIBS += $(EXTRALIBS-$(1))
@ -56,6 +56,7 @@ all: $(AVPROGS)
fftools/ffprobe.o fftools/cmdutils.o: libavutil/ffversion.h | fftools
OUTDIRS += fftools
OUTDIRS += fftools/textformat
OUTDIRS += fftools/resources
ifdef AVPROGS
install: install-progs install-data

4
fftools/resources/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.html.c
*.css.c
*.html.gz
*.css.gz

View File

@ -0,0 +1,13 @@
clean::
$(RM) $(CLEANSUFFIXES:%=fftools/resources/%)
vpath %.html $(SRC_PATH)
vpath %.css $(SRC_PATH)
# Uncomment to prevent deletion during build
#.PRECIOUS: %.css.c %.css.min %.css.gz %.css.min.gz %.html.gz %.html.c
OBJS-resman += \
fftools/resources/resman.o \
fftools/resources/graph.html.o \
fftools/resources/graph.css.o \

353
fftools/resources/graph.css Normal file
View File

@ -0,0 +1,353 @@
/* Variables */
.root {
--ff-colvideo: #6eaa7b;
--ff-colaudio: #477fb3;
--ff-colsubtitle: #ad76ab;
--ff-coltext: #666;
}
/* Common & Misc */
.ff-inputfiles rect, .ff-outputfiles rect, .ff-inputstreams rect, .ff-outputstreams rect, .ff-decoders rect, .ff-encoders rect {
stroke-width: 0;
stroke: transparent;
filter: none !important;
fill: transparent !important;
display: none !important;
}
.cluster span {
color: var(--ff-coltext);
}
.cluster rect {
stroke: #dfdfdf !important;
transform: translateY(-2.3rem);
filter: drop-shadow(1px 2px 2px rgba(185,185,185,0.2)) !important;
rx: 8;
ry: 8;
}
.cluster-label {
font-size: 1.1rem;
}
.cluster-label .nodeLabel {
display: block;
font-weight: 500;
color: var(--ff-coltext);
}
.cluster-label div {
max-width: unset !important;
padding: 3px;
}
.cluster-label foreignObject {
transform: translateY(-0.7rem);
}
/* Input and output files */
.node.ff-inputfile .label foreignObject, .node.ff-outputfile .label foreignObject {
overflow: visible;
}
.cluster.ff-inputfile .cluster-label foreignObject div:not(foreignObject div div), .cluster.ff-outputfile .cluster-label foreignObject div:not(foreignObject div div) {
display: table !important;
}
.nodeLabel div.ff-inputfile, .nodeLabel div.ff-outputfile {
font-size: 1.1rem;
font-weight: 500;
min-width: 14rem;
width: 100%;
display: flex;
color: var(--ff-coltext);
margin-top: 0.1rem;
line-height: 1.35;
padding-bottom: 1.9rem;
}
.nodeLabel div.ff-outputfile {
flex-direction: row-reverse;
}
.ff-inputfile .index, .ff-outputfile .index {
order: 2;
color: var(--ff-coltext);
text-align: center;
border-radius: 0.45rem;
border: 0.18em solid #666666db;
font-weight: 600;
padding: 0 0.3em;
opacity: 0.8;
}
.ff-inputfile .index::before {
content: 'In ';
}
.ff-outputfile .index::before {
content: 'Out ';
}
.ff-inputfile .demuxer_name, .ff-outputfile .muxer_name {
flex: 1;
order: 1;
font-size: 0.9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
max-width: 8rem;
align-content: center;
margin: 0.2rem 0.4rem 0 0.4rem;
}
.ff-inputfile .file_extension, .ff-outputfile .file_extension {
order: 0;
background-color: #888;
color: white;
text-align: center;
border-radius: 0.45rem;
font-weight: 600;
padding: 0 0.4em;
align-content: center;
opacity: 0.8;
}
.ff-inputfile .url, .ff-outputfile .url {
order: 4;
text-align: center;
position: absolute;
left: 0;
right: 0;
bottom: 0.75rem;
font-size: 0.7rem;
font-weight: 400;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0 0.3rem;
direction: rtl;
color: #999;
}
.cluster.ff-inputfile rect, .cluster.ff-outputfile rect {
transform: translateY(-1.8rem);
fill: url(#ff-radgradient);
}
/* Input and output streams */
.node.ff-inputstream rect, .node.ff-outputstream rect {
padding: 0 !important;
margin: 0 !important;
border: none !important;
fill: white;
stroke: #e5e5e5 !important;
height: 2.7rem;
transform: translateY(0.2rem);
filter: none;
rx: 3;
ry: 3;
}
.node.ff-inputstream .label foreignObject, .node.ff-outputstream .label foreignObject {
transform: translateY(-0.2%);
overflow: visible;
}
.node.ff-inputstream .label foreignObject div:not(foreignObject div div), .node.ff-outputstream .label foreignObject div:not(foreignObject div div) {
display: block !important;
line-height: 1.5 !important;
}
.nodeLabel div.ff-inputstream, .nodeLabel div.ff-outputstream {
font-size: 1.0rem;
font-weight: 500;
min-width: 12rem;
width: 100%;
display: flex;
}
.nodeLabel div.ff-outputstream {
flex-direction: row-reverse;
}
.ff-inputstream .name, .ff-outputstream .name {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
align-content: center;
margin-bottom: -0.15rem;
}
.ff-inputstream .index, .ff-outputstream .index {
flex: 0 0 1.4rem;
background-color: #888;
color: white;
text-align: center;
border-radius: 0.3rem;
font-weight: 600;
margin-right: -0.3rem;
margin-left: 0.4rem;
opacity: 0.8;
}
.ff-outputstream .index {
margin-right: 0.6rem;
margin-left: -0.4rem;
}
.ff-inputstream::before, .ff-outputstream::before {
font-variant-emoji: text;
flex: 0 0 2rem;
margin-left: -0.8rem;
margin-right: 0.2rem;
}
.ff-outputstream::before {
margin-left: 0.2rem;
margin-right: -0.6rem;
}
.ff-inputstream.video::before, .ff-outputstream.video::before {
content: '\239A';
color: var(--ff-colvideo);
font-size: 2.25rem;
line-height: 0.5;
font-weight: bold;
}
.ff-inputstream.audio::before, .ff-outputstream.audio::before {
content: '\1F39D';
color: var(--ff-colaudio);
font-size: 1.75rem;
line-height: 0.9;
}
.ff-inputstream.subtitle::before, .ff-outputstream.subtitle::before {
content: '\1AC';
color: var(--ff-colsubtitle);
font-size: 1.2rem;
line-height: 1.1;
transform: scaleX(1.5);
margin-top: 0.050rem;
}
.ff-inputstream.attachment::before, .ff-outputstream.attachment::before {
content: '\1F4CE';
font-size: 1.3rem;
line-height: 1.15;
}
.ff-inputstream.data::before, .ff-outputstream.data::before {
content: '\27E8\2219\2219\2219\27E9';
font-size: 1.15rem;
line-height: 1.17;
letter-spacing: -0.3px;
}
/* Filter Graphs */
.cluster.ff-filters rect {
stroke-dasharray: 6 !important;
stroke-width: 1.3px;
stroke: #d1d1d1 !important;
filter: none !important;
}
.cluster.ff-filters div.ff-filters .id {
display: none;
}
.cluster.ff-filters div.ff-filters .name {
margin-right: 0.5rem;
font-size: 0.9rem;
}
.cluster.ff-filters div.ff-filters .description {
font-weight: 400;
font-size: 0.75rem;
vertical-align: middle;
color: #777;
font-family: Cascadia Code, Lucida Console, monospace;
}
/* Filter Shapes */
.node.ff-filter rect {
rx: 10;
ry: 10;
stroke-width: 1px;
stroke: #d3d3d3;
fill: url(#ff-filtergradient);
filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.1));
}
.node.ff-filter .label foreignObject {
transform: translateY(-0.4rem);
overflow: visible;
}
.nodeLabel div.ff-filter {
font-size: 1.0rem;
font-weight: 500;
text-transform: uppercase;
min-width: 5.5rem;
margin-bottom: 0.5rem;
}
.nodeLabel div.ff-filter span {
color: inherit;
}
/* Decoders & Encoders */
.node.ff-decoder rect, .node.ff-encoder rect {
stroke-width: 1px;
stroke: #d3d3d3;
fill: url(#ff-filtergradient);
filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.1));
}
.nodeLabel div.ff-decoder, .nodeLabel div.ff-encoder {
font-size: 0.85rem;
font-weight: 500;
min-width: 3.5rem;
}
/* Links and Arrows */
path.flowchart-link[id|='video'] {
stroke: var(--ff-colvideo);
}
path.flowchart-link[id|='audio'] {
stroke: var(--ff-colaudio);
}
path.flowchart-link[id|='subtitle'] {
stroke: var(--ff-colsubtitle);
}
marker.marker path {
fill: context-stroke;
}
.edgeLabel foreignObject {
transform: translateY(-1rem);
}
.edgeLabel p {
background: transparent;
white-space: nowrap;
margin: 1rem 0.5rem !important;
font-weight: 500;
color: var(--ff-coltext);
}
.edgeLabel, .labelBkg {
background: transparent;
}
.edgeLabels .edgeLabel * {
font-size: 0.8rem;
}

View File

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>FFmpeg Graph</title>
</head>
<body>
<style>
html {
color: #666;
font-family: Roboto;
height: 100%;
}
body {
background-color: #f9f9f9;
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
margin: 0;
padding: 1.7rem 1.7rem 3.5rem 1.7rem;
}
div#banner {
align-items: center;
display: flex;
flex-direction: row;
margin-bottom: 1.5rem;
margin-left: 0.6vw;
}
div#header {
aspect-ratio: 1/1;
background-image: url(https://trac.ffmpeg.org/ffmpeg-logo.png);
background-size: cover;
width: 1.6rem;
}
h1 {
font-size: 1.2rem;
margin: 0 0.5rem;
}
pre.mermaid {
align-items: center;
background-color: white;
box-shadow: 2px 2px 25px 0px #00000010;
color: transparent;
display: flex;
flex: 1;
justify-content: center;
margin: 0;
overflow: overlay;
}
pre.mermaid svg {
height: auto;
margin: 0;
max-width: unset !important;
width: auto;
}
pre.mermaid svg * {
user-select: none;
}
</style>
<div id="banner">
<div id="header"></div>
<h1>FFmpeg Execution Graph</h1>
</div>
<pre class="mermaid">
__###__
</pre>
<script type="module">
import vanillaJsWheelZoom from 'https://cdn.jsdelivr.net/npm/vanilla-js-wheel-zoom@9.0.4/+esm';
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
function initViewer() {
var element = document.querySelector('.mermaid svg')
vanillaJsWheelZoom.create('pre.mermaid svg', { type: 'html', smoothTimeDrag: 0, width: element.clientWidth, height: element.clientHeight, maxScale: 3 });
}
mermaid.initialize({ startOnLoad: false });
document.fonts.ready.then(() => { mermaid.run({ querySelector: '.mermaid', postRenderCallback: initViewer }); });
</script>
</body>
</html>

231
fftools/resources/resman.c Normal file
View File

@ -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;
#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;
}

View File

@ -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"
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 */