From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <ffmpeg-devel-bounces@ffmpeg.org> Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id A526A4E84B for <ffmpegdev@gitmailbox.com>; Thu, 8 May 2025 01:39:47 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 28A8668C306; Thu, 8 May 2025 04:39:01 +0300 (EEST) Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 29C8868BD3C for <ffmpeg-devel@ffmpeg.org>; Thu, 8 May 2025 04:38:11 +0300 (EEST) Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-22fa47d6578so1066135ad.2 for <ffmpeg-devel@ffmpeg.org>; Wed, 07 May 2025 18:38:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1746668289; x=1747273089; darn=ffmpeg.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date :references:in-reply-to:message-id:from:from:to:cc:subject:date :message-id:reply-to; bh=CXkOwRCT9ll+n6mI3d1azrdufd6C6SRc5e9hyb+X6jw=; b=ZAX9+1nKCSmj5bnvu29CRnBbXiu1jaxiAUFMi7jgNK9KEPhFKZfpyTWvq9O9WFRXgN bvM1Syvlhd79kpfMMJlEyA3QO7R+1wRaJ3jY8kdNu0jfyf1RXfhTa+PWz0oVTbLVvCNv EV+uvrYXLJAkIGHJTdBF3sJVm/Po+VHDzRQqYfgoD2IvWzkSj7cJ6ezv0w16Z9ariEeo ipQ1Y89VtdvQiEU5sg5AqPcrBO+q94L0D5KQHGIPkAxDTvn2QwIi0GJPJBo1qHT/aYiz oIWwJ/ctbS0Pcgcwh6H4KGAU/D0uCBiI55FEWiaK/xH/k3KlVWyRylGCgb+kuToW7PYE /Neg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1746668289; x=1747273089; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date :references:in-reply-to:message-id:from:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=CXkOwRCT9ll+n6mI3d1azrdufd6C6SRc5e9hyb+X6jw=; b=QeihuGN6l/jevJ1WliuGgmE4BNnY+EixLqulKSEW/1S/zO/fBSAI/QWXf/u8yg4t8f UZqvykllOcchfj59zLoF1DMdWCeB84DVC+MeZYwS/mTVakYNLEDIXpaSY5gG3DbDLCiA 7V9feVWs2jtrlU5pni48yb550jizMtwbqrmujvuDXMcEJxMn/i9PcqCgD/d3VMs5arl4 zA2OJxB0GYMPOHGGRBQCQ37AXwEN8NCR0bPwIFtk4degxrt9ThAoOxR+X+swidMetiaV uatEw48QzVK4ucna5B9ik+FIOBHnWM0o2DixEe2bjLEu8jNiC7OA+41abJ8NYsZPjXF9 SzQw== X-Gm-Message-State: AOJu0Yw32trTdBDVLBNK+cNtVUuW5iPezWiTOQIR0KDllYuaqW/ZB0e9 5eHkR7PcCSxcQWtqhCzcJS8prp85ql0HQT35CeLYCWhiHFwaSk8zhjrmzw== X-Gm-Gg: ASbGncvjlOyQVclITgD9y+ghdwQAiCUyOsR1DMjRTWJtufvHW70rfr6zNbqy5zSp3WE 0sx5GuKaEsA1v7dc2T5CVsbrgUUCi9+I7nHyIhkwDUzLMjP/0gn7BaepPBRxa8ZJG7mM7P8oO46 YwRGX/5YpWmgTDadUD7Qy1dvGFkAOgQfnOZp0h3vZj+C/r6PH/iILdcbSl3z+P+bwRjoS9JpHKj +abL9hfTIf8RqdZO7N974EZnnp4TVb9/SmGzp5DhChdg5ffp2FZgWfJFDILZmOSucnrJ1dd18zD jIu6lZ3D0Z/jsmYIDlDxwYaUsQKDNUDavjYusI2FGhS3fFlkzRUohGysM3fARIMb8tnDmw== X-Google-Smtp-Source: AGHT+IGF5VeUjO2aiS9YFZoIPovvCe8OlKr5ClGwdqy8f4fmh2WJr+Mo+fgH/Insjgq0jEfTUSIHlA== X-Received: by 2002:a17:903:22cd:b0:223:7006:4db2 with SMTP id d9443c01a7336-22e5ec9eb02mr81989375ad.31.1746668289220; Wed, 07 May 2025 18:38:09 -0700 (PDT) Received: from [127.0.0.1] (master.gitmailbox.com. [34.83.118.50]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22e15516c19sm101874665ad.44.2025.05.07.18.38.08 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 07 May 2025 18:38:08 -0700 (PDT) From: softworkz <ffmpegagent@gmail.com> X-Google-Original-From: softworkz <softworkz@hotmail.com> Message-Id: <ed92ac7577fe0aa3d24f6d33a53fbd16b180fcdc.1746668216.git.ffmpegagent@gmail.com> In-Reply-To: <pull.66.v12.ffstaging.FFmpeg.1746668216.ffmpegagent@gmail.com> References: <pull.66.v11.ffstaging.FFmpeg.1746398104.ffmpegagent@gmail.com> <pull.66.v12.ffstaging.FFmpeg.1746668216.ffmpegagent@gmail.com> Date: Thu, 08 May 2025 01:36:53 +0000 Fcc: Sent MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH v12 12/15] fftools/resources: Add resource manager files with build-time compression X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches <ffmpeg-devel.ffmpeg.org> List-Unsubscribe: <https://ffmpeg.org/mailman/options/ffmpeg-devel>, <mailto:ffmpeg-devel-request@ffmpeg.org?subject=unsubscribe> List-Archive: <https://ffmpeg.org/pipermail/ffmpeg-devel> List-Post: <mailto:ffmpeg-devel@ffmpeg.org> List-Help: <mailto:ffmpeg-devel-request@ffmpeg.org?subject=help> List-Subscribe: <https://ffmpeg.org/mailman/listinfo/ffmpeg-devel>, <mailto:ffmpeg-devel-request@ffmpeg.org?subject=subscribe> Reply-To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Cc: softworkz <softworkz@hotmail.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" <ffmpeg-devel-bounces@ffmpeg.org> Archived-At: <https://master.gitmailbox.com/ffmpegdev/ed92ac7577fe0aa3d24f6d33a53fbd16b180fcdc.1746668216.git.ffmpegagent@gmail.com/> List-Archive: <https://master.gitmailbox.com/ffmpegdev/> List-Post: <mailto:ffmpegdev@gitmailbox.com> From: softworkz <softworkz@hotmail.com> 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> --- 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(-) 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/configure b/configure index 2e69b3c56c..0609dac4ab 100755 --- a/configure +++ b/configure @@ -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 diff --git a/ffbuild/common.mak b/ffbuild/common.mak index ca45a0f368..0e1eb1f62b 100644 --- a/ffbuild/common.mak +++ b/ffbuild/common.mak @@ -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 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..8579a52678 --- /dev/null +++ b/fftools/resources/Makefile @@ -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 \ 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..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; + + +#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; +} 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-request@ffmpeg.org with subject "unsubscribe".