From: softworkz <softwo...@hotmail.com> Signed-off-by: softworkz <softwo...@hotmail.com> --- ffbuild/common.mak | 28 ++- fftools/Makefile | 3 +- fftools/resources/.gitignore | 4 + fftools/resources/Makefile | 27 +++ fftools/resources/graph.css | 353 +++++++++++++++++++++++++++++++++++ fftools/resources/graph.html | 86 +++++++++ fftools/resources/resman.c | 213 +++++++++++++++++++++ fftools/resources/resman.h | 50 +++++ 8 files changed, 762 insertions(+), 2 deletions(-) create mode 100644 fftools/resources/.gitignore create mode 100644 fftools/resources/Makefile create mode 100644 fftools/resources/graph.css create mode 100644 fftools/resources/graph.html create mode 100644 fftools/resources/resman.c create mode 100644 fftools/resources/resman.h
diff --git a/ffbuild/common.mak b/ffbuild/common.mak index ca45a0f368..6717092d44 100644 --- a/ffbuild/common.mak +++ b/ffbuild/common.mak @@ -139,6 +139,32 @@ 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/ $$//' \ + > $@ + +# 2) Gzip the minified CSS +%.css.min.gz: %.css.min + $(M)gzip -nc9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) >$@ + +# 3) Convert the gzipped CSS to a .c array +%.css.c: %.css.min.gz $(BIN2CEXE) + $(BIN2C) $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) $@ $(subst .,_,$(basename $(notdir $@))) + +# 4) Gzip the HTML file (no minification needed) +%.html.gz: TAG = GZIP +%.html.gz: %.html + $(M)gzip -nc9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) > $@ + +# 5) Convert the gzipped HTML to a .c array +%.html.c: %.html.gz $(BIN2CEXE) + $(BIN2C) $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) $@ $(subst .,_,$(basename $(notdir $@))) + clean:: $(RM) $(BIN2CEXE) $(CLEANSUFFIXES:%=ffbuild/%) @@ -214,7 +240,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 diff --git a/fftools/Makefile b/fftools/Makefile index e9c9891c34..a30bec889e 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -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 diff --git a/fftools/resources/.gitignore b/fftools/resources/.gitignore new file mode 100644 index 0000000000..5f496535a6 --- /dev/null +++ b/fftools/resources/.gitignore @@ -0,0 +1,4 @@ +*.html.c +*.css.c +*.html.gz +*.css.gz diff --git a/fftools/resources/Makefile b/fftools/resources/Makefile new file mode 100644 index 0000000000..f3a0d0a970 --- /dev/null +++ b/fftools/resources/Makefile @@ -0,0 +1,27 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=fftools/resources/%) + + +HTML_RESOURCES := fftools/resources/graph.html \ + +# .html => (gzip) .html.gz => (bin2c) .html.c => (cc) .o +HTML_RESOURCES_GZ := $(HTML_RESOURCES:.html=.html.gz) +HTML_RESOURCES_C := $(HTML_RESOURCES_GZ:.html.gz=.html.c) +HTML_RESOURCES_OBJS := $(HTML_RESOURCES_C:.c=.o) + +CSS_RESOURCES := fftools/resources/graph.css \ + +# .css => (sh) .css.min => (gzip) .css.min.gz => (bin2c) .css.c => (cc) .o +CSS_RESOURCES_MIN := $(CSS_RESOURCES:.css=.css.min) +CSS_RESOURCES_GZ := $(CSS_RESOURCES_MIN:.css.min=.css.min.gz) +CSS_RESOURCES_C := $(CSS_RESOURCES_GZ:.css.min.gz=.css.c) +CSS_RESOURCES_OBJS := $(CSS_RESOURCES_C:.c=.o) + +# Uncomment to prevent deletion +#.PRECIOUS: %.css.c %.css.min %.css.gz %.css.min.gz + +OBJS-resman += \ + fftools/resources/resman.o \ + $(HTML_RESOURCES_OBJS) \ + $(CSS_RESOURCES_OBJS) \ + diff --git a/fftools/resources/graph.css b/fftools/resources/graph.css new file mode 100644 index 0000000000..ab480673ab --- /dev/null +++ b/fftools/resources/graph.css @@ -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; +} diff --git a/fftools/resources/graph.html b/fftools/resources/graph.html new file mode 100644 index 0000000000..cd94276fd4 --- /dev/null +++ b/fftools/resources/graph.html @@ -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> \ No newline at end of file diff --git a/fftools/resources/resman.c b/fftools/resources/resman.c new file mode 100644 index 0000000000..488aaeecf6 --- /dev/null +++ b/fftools/resources/resman.c @@ -0,0 +1,213 @@ +/* + * 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> +#include <zlib.h> +#include "resman.h" +#include <libavformat/url.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 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; +} + +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) { + char *out = NULL; + size_t out_len; + int dict_ret; + + 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); + 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" + +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 */ -- ffmpeg-codebot _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".