[FFmpeg-devel] [PATCH v5 00/14] Execution Graph Printing
ffmpegagent
ffmpegagent at gmail.com
Wed Apr 23 00:55:29 EEST 2025
Shortest cover letter for my longest-running FFmpeg patchset:
* Apply
* Build
* Add the "-sg" switch to any FFmpeg command line
* Press 'q' when you don't want to wait
SG = Show Graph
Documentation and examples can be found here:
https://github.com/softworkz/ffmpeg_output_apis/wiki
Version Updates
===============
V2
==
* Rebased on top of Andreas' improvements
* Applied changes from review (thanks, Andreas)
V3
==
* Fixed all "new warnings"
* Fixed out-of-tree building (thanks, Michael)
V4
==
* Resolved merge conflict
* Fixed build on MinGW (missing include due to WIN32_LEAN_AND_MEAN being
defined) (thanks, Michael)
V5
==
* Applied changes as per review from Stefano (thanks!)
* Introduced AVTextFormatOptions struct for options in
avtext_context_open()
.
softworkz (14):
fftools/textformat: Formatting and whitespace changes
fftools/textformat: Apply quality improvements
fftools/avtextformat: Re-use BPrint in loop
fftools/textformat: Introduce AVTextFormatOptions for
avtext_context_open()
fftools/textformat: Introduce common header and deduplicate code
fftools/textformat: AVTextWriter change writer_printf signature
fftools/tf_internal: Use av_default_item_name
fftools/textformat: Add function avtext_print_integer_flags()
fftools/ffmpeg_filter: Move some declaration to new header file
avfilter/avfilter: Add avfilter_link_get_hw_frames_ctx()
fftools/resources: Add resource manager files
fftools/ffmpeg_mux: Make ms_from_ost() inline
fftools/graphprint: Add execution graph printing
fftools/graphprint: Now, make it a Killer-Feature!
doc/APIchanges | 3 +
doc/ffmpeg.texi | 14 +
ffbuild/common.mak | 28 +-
fftools/Makefile | 22 +-
fftools/ffmpeg.c | 4 +
fftools/ffmpeg.h | 4 +
fftools/ffmpeg_filter.c | 195 +----
fftools/ffmpeg_filter.h | 234 ++++++
fftools/ffmpeg_mux.h | 2 +-
fftools/ffmpeg_opt.c | 17 +
fftools/ffprobe.c | 13 +-
fftools/graph/filelauncher.c | 205 +++++
fftools/graph/graphprint.c | 1147 ++++++++++++++++++++++++++++
fftools/graph/graphprint.h | 62 ++
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 ++
fftools/textformat/avtextformat.c | 229 +++---
fftools/textformat/avtextformat.h | 73 +-
fftools/textformat/avtextwriters.h | 11 +-
fftools/textformat/tf_compact.c | 121 +--
fftools/textformat/tf_default.c | 55 +-
fftools/textformat/tf_flat.c | 51 +-
fftools/textformat/tf_ini.c | 62 +-
fftools/textformat/tf_internal.h | 81 ++
fftools/textformat/tf_json.c | 56 +-
fftools/textformat/tf_mermaid.c | 658 ++++++++++++++++
fftools/textformat/tf_mermaid.h | 41 +
fftools/textformat/tf_xml.c | 68 +-
fftools/textformat/tw_avio.c | 18 +-
fftools/textformat/tw_buffer.c | 7 +-
fftools/textformat/tw_stdout.c | 8 +-
libavfilter/avfilter.c | 9 +
libavfilter/avfilter.h | 12 +
37 files changed, 3686 insertions(+), 557 deletions(-)
create mode 100644 fftools/ffmpeg_filter.h
create mode 100644 fftools/graph/filelauncher.c
create mode 100644 fftools/graph/graphprint.c
create mode 100644 fftools/graph/graphprint.h
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
create mode 100644 fftools/textformat/tf_internal.h
create mode 100644 fftools/textformat/tf_mermaid.c
create mode 100644 fftools/textformat/tf_mermaid.h
base-commit: 9e1162bdf1454d7ae3737429bcc6bd66e5da303a
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-66%2Fsoftworkz%2Fsubmit_print_execution_graph-v5
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-66/softworkz/submit_print_execution_graph-v5
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/66
Range-diff vs v4:
1: dbd9193a0d ! 1: 0672fc41e7 fftools/textformat: Formatting and whitespace changes
@@ fftools/textformat/avtextformat.c: static const char *textcontext_get_formatter_
};
@@ fftools/textformat/avtextformat.c: void avtext_context_close(AVTextFormatContext **ptctx)
- }
--int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args,
+ int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args,
- const struct AVTextFormatSection *sections, int nb_sections,
-- int show_value_unit,
-- int use_value_prefix,
-- int use_byte_value_binary_prefix,
-- int use_value_sexagesimal_format,
-- int show_optional_fields,
-- char *show_data_hash)
-+int avtext_context_open(AVTextFormatContext **ptctx,
-+ const AVTextFormatter *formatter,
-+ AVTextWriterContext *writer_context,
-+ const char *args,
-+ const AVTextFormatSection *sections,
-+ int nb_sections,
-+ int show_value_unit,
-+ int use_value_prefix,
-+ int use_byte_value_binary_prefix,
-+ int use_value_sexagesimal_format,
-+ int show_optional_fields,
-+ char *show_data_hash)
- {
- AVTextFormatContext *tctx;
- int i, ret = 0;
++ const AVTextFormatSection *sections, int nb_sections,
+ int show_value_unit,
+ int use_value_prefix,
+ int use_byte_value_binary_prefix,
@@ fftools/textformat/avtextformat.c: int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *form
av_dict_free(&opts);
}
2: 1516e67a5b ! 2: 1e312f4685 fftools/textformat: Quality improvements
@@ Metadata
Author: softworkz <softworkz at hotmail.com>
## Commit message ##
- fftools/textformat: Quality improvements
+ fftools/textformat: Apply quality improvements
+
+ Perform multiple improvements to increase code robustness.
+ In particular:
+ - favor unsigned counters for loops
+ - add missing checks
+ - avoid possibly leaks
+ - move variable declarations to inner scopes when feasible
+ - provide explicit type-casting when needed
Signed-off-by: softworkz <softworkz at hotmail.com>
@@ fftools/textformat/avtextformat.c: static const AVClass textcontext_class = {
av_bprintf(bp, "%02X", ubuf[i]);
}
-@@ fftools/textformat/avtextformat.c: int avtext_context_open(AVTextFormatContext **ptctx,
+@@ fftools/textformat/avtextformat.c: int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *form
AVTextFormatContext *tctx;
int i, ret = 0;
-- if (!(tctx = av_mallocz(sizeof(AVTextFormatContext)))) {
+ if (!ptctx || !formatter)
+ return AVERROR(EINVAL);
+
-+ if (!((tctx = av_mallocz(sizeof(AVTextFormatContext))))) {
+ if (!(tctx = av_mallocz(sizeof(AVTextFormatContext)))) {
ret = AVERROR(ENOMEM);
goto fail;
- }
-@@ fftools/textformat/avtextformat.c: int avtext_context_open(AVTextFormatContext **ptctx,
+@@ fftools/textformat/avtextformat.c: int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *form
av_log(NULL, AV_LOG_ERROR, " %s", n);
av_log(NULL, AV_LOG_ERROR, "\n");
}
@@ fftools/textformat/avtextformat.c: void avtext_print_section_footer(AVTextFormat
- const struct AVTextFormatSection *section = tctx->section[tctx->level];
+ const AVTextFormatSection *section;
+
-+ if (!key || tctx->level < 0 || tctx->level >= SECTION_MAX_NB_LEVELS)
-+ return;
++ av_assert0(key && tctx->level >= 0 && tctx->level < SECTION_MAX_NB_LEVELS);
+
+ section = tctx->section[tctx->level];
if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
tctx->formatter->print_integer(tctx, key, val);
-@@ fftools/textformat/avtextformat.c: void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t va
-
- static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const char *src)
- {
-- const uint8_t *p, *endp;
-+ const uint8_t *p, *endp, *srcp = (const uint8_t *)src;
- AVBPrint dstbuf;
-+ AVBPrint bp;
- int invalid_chars_nb = 0, ret = 0;
-
-+ *dstp = NULL;
- av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
-+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
-
-- endp = src + strlen(src);
-- for (p = src; *p;) {
-- uint32_t code;
-+ endp = srcp + strlen(src);
-+ for (p = srcp; *p;) {
-+ int32_t code;
- int invalid = 0;
- const uint8_t *p0 = p;
-
- if (av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags) < 0) {
-- AVBPrint bp;
-- av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
-- bprint_bytes(&bp, p0, p-p0);
-- av_log(tctx, AV_LOG_DEBUG,
-- "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
-+ av_bprint_clear(&bp);
-+ bprint_bytes(&bp, p0, p - p0);
-+ av_log(tctx, AV_LOG_DEBUG, "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
- invalid = 1;
- }
-
-@@ fftools/textformat/avtextformat.c: static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const
- }
-
- if (!invalid || tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_IGNORE)
-- av_bprint_append_data(&dstbuf, p0, p-p0);
-+ av_bprint_append_data(&dstbuf, (const char *)p0, p - p0);
- }
-
- if (invalid_chars_nb && tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_REPLACE)
-@@ fftools/textformat/avtextformat.c: static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const
-
- end:
- av_bprint_finalize(&dstbuf, dstp);
-+ av_bprint_finalize(&bp, NULL);
- return ret;
- }
-
@@ fftools/textformat/avtextformat.c: struct unit_value {
const char *unit;
};
@@ fftools/textformat/avtextformat.c: void avtext_print_unit_int(AVTextFormatContex
+ const AVTextFormatSection *section;
int ret = 0;
-+ if (!key || !val || tctx->level < 0 || tctx->level >= SECTION_MAX_NB_LEVELS)
-+ return AVERROR(EINVAL);
++ av_assert0(key && val && tctx->level >= 0 && tctx->level < SECTION_MAX_NB_LEVELS);
+
+ section = tctx->section[tctx->level];
+
@@ fftools/textformat/tf_json.c: static const char *json_escape_str(AVBPrint *dst,
const char *p;
+ if (!src) {
-+ av_log(log_ctx, AV_LOG_ERROR, "json_escape_str: NULL source string\n");
++ av_log(log_ctx, AV_LOG_WARNING, "Cannot escape NULL string, returning NULL\n");
+ return NULL;
+ }
+
@@ fftools/textformat/tf_json.c: static const char *json_escape_str(AVBPrint *dst,
av_bprint_chars(dst, *p, 1);
}
@@ fftools/textformat/tf_json.c: static void json_print_section_header(AVTextFormatContext *wctx, const void *dat
- wctx->section[wctx->level-1] : NULL;
-
- if (wctx->level && wctx->nb_item[wctx->level-1])
+ {
+ JSONContext *json = wctx->priv;
+ AVBPrint buf;
+- const struct AVTextFormatSection *section = wctx->section[wctx->level];
+- const struct AVTextFormatSection *parent_section = wctx->level ?
+- wctx->section[wctx->level-1] : NULL;
++ const AVTextFormatSection *section = wctx->section[wctx->level];
++ const AVTextFormatSection *parent_section = wctx->level ? wctx->section[wctx->level - 1] : NULL;
+
+- if (wctx->level && wctx->nb_item[wctx->level-1])
+ if (wctx->level && wctx->nb_item[wctx->level - 1])
writer_put_str(wctx, ",\n");
if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
+@@ fftools/textformat/tf_json.c: static void json_print_str(AVTextFormatContext *wctx, const char *key, const cha
+ static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+ {
+ JSONContext *json = wctx->priv;
+- const struct AVTextFormatSection *parent_section = wctx->level ?
+- wctx->section[wctx->level-1] : NULL;
++ const AVTextFormatSection *parent_section = wctx->level ? wctx->section[wctx->level - 1] : NULL;
+ AVBPrint buf;
+
+ if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
## fftools/textformat/tf_xml.c ##
@@
@@ fftools/textformat/tf_xml.c
#include "avtextformat.h"
## fftools/textformat/tw_avio.c ##
+@@
+ #include <string.h>
+
+ #include "avtextwriters.h"
++#include "libavutil/avassert.h"
+
+ #include "libavutil/error.h"
+
@@ fftools/textformat/tw_avio.c: static void io_w8(AVTextWriterContext *wctx, int b)
static void io_put_str(AVTextWriterContext *wctx, const char *str)
{
@@ fftools/textformat/tw_avio.c: const AVTextWriter avtextwriter_avio = {
int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename)
{
-+ if (!output_filename || !output_filename[0])
++ if (!output_filename || !output_filename[0]) {
++ av_log(NULL, AV_LOG_ERROR, "The output_filename cannot be NULL or empty\n");
+ return AVERROR(EINVAL);
++ }
+
IOWriterContext *ctx;
int ret;
@@ fftools/textformat/tw_avio.c: int avtextwriter_create_file(AVTextWriterContext *
int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit)
{
-+ if (!pwctx || !avio_ctx)
-+ return AVERROR(EINVAL);
++ av_assert0(avio_ctx);
+
IOWriterContext *ctx;
int ret;
-: ---------- > 3: c71836fce0 fftools/avtextformat: Re-use BPrint in loop
-: ---------- > 4: 26be409371 fftools/textformat: Introduce AVTextFormatOptions for avtext_context_open()
3: 7024548b92 ! 5: 89da2c883e fftools/textformat: Introduce common header and deduplicate code
@@ Commit message
Signed-off-by: softworkz <softworkz at hotmail.com>
- ## fftools/textformat/avtextwriters.h ##
-@@ fftools/textformat/avtextwriters.h: typedef struct AVTextWriter {
- void (*uninit)(AVTextWriterContext *wctx);
- void (*writer_w8)(AVTextWriterContext *wctx, int b);
- void (*writer_put_str)(AVTextWriterContext *wctx, const char *str);
-- void (*writer_printf)(AVTextWriterContext *wctx, const char *fmt, ...);
-+ void (*writer_printf)(AVTextWriterContext *wctx, const char *fmt, va_list vl);
- } AVTextWriter;
-
- typedef struct AVTextWriterContext {
-
## fftools/textformat/tf_compact.c ##
@@
#include "libavutil/bprint.h"
@@ fftools/textformat/tf_json.c: static const char *json_escape_str(AVBPrint *dst,
+ const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
JSONContext *json = wctx->priv;
AVBPrint buf;
-- const struct AVTextFormatSection *section = wctx->section[wctx->level];
-- const struct AVTextFormatSection *parent_section = wctx->level ?
-- wctx->section[wctx->level-1] : NULL;
-
-- if (wctx->level && wctx->nb_item[wctx->level-1])
+- const AVTextFormatSection *section = wctx->section[wctx->level];
+- const AVTextFormatSection *parent_section = wctx->level ? wctx->section[wctx->level - 1] : NULL;
++
+ if (!section)
+ return;
-+
+
if (wctx->level && wctx->nb_item[wctx->level - 1])
writer_put_str(wctx, ",\n");
-
@@ fftools/textformat/tf_json.c: static void json_print_section_header(AVTextFormatContext *wctx, const void *dat
static void json_print_section_footer(AVTextFormatContext *wctx)
@@ fftools/textformat/tf_json.c: static void json_print_str(AVTextFormatContext *wc
{
+ const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
JSONContext *json = wctx->priv;
-- const struct AVTextFormatSection *parent_section = wctx->level ?
-- wctx->section[wctx->level-1] : NULL;
+- const AVTextFormatSection *parent_section = wctx->level ? wctx->section[wctx->level - 1] : NULL;
AVBPrint buf;
if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
@@ fftools/textformat/tf_xml.c: const AVTextFormatter avtextformatter_xml = {
.priv_class = &xml_class,
};
-
-
- ## fftools/textformat/tw_avio.c ##
-@@ fftools/textformat/tw_avio.c: static void io_put_str(AVTextWriterContext *wctx, const char *str)
- avio_write(ctx->avio_context, (const unsigned char *)str, (int)strlen(str));
- }
-
--static void io_printf(AVTextWriterContext *wctx, const char *fmt, ...)
-+static void io_printf(AVTextWriterContext *wctx, const char *fmt, va_list vl)
- {
- IOWriterContext *ctx = wctx->priv;
-- va_list ap;
-
-- va_start(ap, fmt);
-- avio_vprintf(ctx->avio_context, fmt, ap);
-- va_end(ap);
-+ avio_vprintf(ctx->avio_context, fmt, vl);
- }
-
-
-
- ## fftools/textformat/tw_buffer.c ##
-@@ fftools/textformat/tw_buffer.c: static void buffer_put_str(AVTextWriterContext *wctx, const char *str)
- av_bprintf(ctx->buffer, "%s", str);
- }
-
--static void buffer_printf(AVTextWriterContext *wctx, const char *fmt, ...)
-+static void buffer_printf(AVTextWriterContext *wctx, const char *fmt, va_list vl)
- {
- BufferWriterContext *ctx = wctx->priv;
-
-- va_list vargs;
-- va_start(vargs, fmt);
-- av_vbprintf(ctx->buffer, fmt, vargs);
-- va_end(vargs);
-+ av_vbprintf(ctx->buffer, fmt, vl);
- }
-
-
-
- ## fftools/textformat/tw_stdout.c ##
-@@ fftools/textformat/tw_stdout.c: static inline void stdout_put_str(AVTextWriterContext *wctx, const char *str)
- printf("%s", str);
- }
-
--static inline void stdout_printf(AVTextWriterContext *wctx, const char *fmt, ...)
-+static inline void stdout_printf(AVTextWriterContext *wctx, const char *fmt, va_list vl)
- {
-- va_list ap;
--
-- va_start(ap, fmt);
-- vprintf(fmt, ap);
-- va_end(ap);
-+ vprintf(fmt, vl);
- }
-
-
-: ---------- > 6: ecf6f061b2 fftools/textformat: AVTextWriter change writer_printf signature
4: 4f1218b594 ! 7: c190f79565 fftools/tf_internal: Use ac_default_item_name
@@ Metadata
Author: softworkz <softworkz at hotmail.com>
## Commit message ##
- fftools/tf_internal: Use ac_default_item_name
+ fftools/tf_internal: Use av_default_item_name
Signed-off-by: softworkz <softworkz at hotmail.com>
5: c1fea3027a = 8: 1fe4a8fe6c fftools/textformat: Add function avtext_print_integer_flags()
6: 28aeb7180d = 9: ba034ef3b1 fftools/ffmpeg_filter: Move some declaration to new header file
7: d2ad11ac85 = 10: 6e31aa603a avfilter/avfilter: Add avfilter_link_get_hw_frames_ctx()
8: 9fa93efdda = 11: ea2d41b048 fftools/resources: Add resource manager files
9: 45b2a38592 = 12: 4fa179848a fftools/ffmpeg_mux: Make ms_from_ost() inline
10: 0e52640aca ! 13: 62d4cab294 fftools/graphprint: Add execution graph printing
@@ fftools/graph/graphprint.c (new)
+ goto fail;
+ }
+
-+ ret = avtext_context_open(&tfc, text_formatter, wctx, w_args, sections, FF_ARRAY_ELEMS(sections), 0, 0, 0, 0, -1, NULL);
++ AVTextFormatOptions tf_options = { .show_optional_fields = -1 };
++ ret = avtext_context_open(&tfc, text_formatter, wctx, w_args, sections, FF_ARRAY_ELEMS(sections), tf_options, NULL);
+ if (ret < 0) {
+ goto fail;
+ }
11: 07a8d731ee = 14: 35fc23039e fftools/graphprint: Now, make it a Killer-Feature!
--
ffmpeg-codebot
More information about the ffmpeg-devel
mailing list