From eecebb8da4094472ddda35bfcef3e9337e9ce92f Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt Date: Sun, 1 Jun 2025 00:13:54 +0200 Subject: [PATCH v2 02/11] fftools/textformat/avtextformat: Separate mutable and immutable data Only two fields of AVTextFormatSection are ever modified: entries_to_show and show_all_entries (they are only used by ffprobe; the graph printing code always prints everything). These fields do not belong into AVTextFormatSection, they are more ffprobe-internal (and if the graph printing code ever made use of them, these fields could very well be per GraphPrintContext). This commit therefore moves them out of AVTextFormatSection and adds a callback to AVTextFormatContext to decide which elements to discard. This also allows to make the AVTextFormatSections const. This also fixes a race when initializing the sections for graphprint. Signed-off-by: Andreas Rheinhardt --- fftools/ffprobe.c | 38 +++++++++++++++++++++++-------- fftools/graph/graphprint.c | 9 +------- fftools/textformat/avtextformat.c | 9 +++----- fftools/textformat/avtextformat.h | 13 +++++++++-- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index 80ce38e73b..77a0ea67bf 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -246,7 +246,7 @@ static const char *get_stream_group_type(const void *data) return av_x_if_null(avformat_stream_group_name(stg->type), "unknown"); } -static struct AVTextFormatSection sections[] = { +static const AVTextFormatSection sections[] = { [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } }, [SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } }, [SECTION_ID_CHAPTER_TAGS] = { SECTION_ID_CHAPTER_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" }, @@ -318,6 +318,13 @@ static struct AVTextFormatSection sections[] = { [SECTION_ID_SUBTITLE] = { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } }, }; +typedef struct EntrySelection { + int show_all_entries; + AVDictionary *entries_to_show; +} EntrySelection; + +static EntrySelection selected_entries[FF_ARRAY_ELEMS(sections)] = { 0 }; + static const OptionDef *options; /* FFprobe context */ @@ -352,6 +359,14 @@ typedef struct LogBuffer { static LogBuffer *log_buffer; static int log_buffer_size; +static int is_key_selected_callback(AVTextFormatContext *tctx, const char *key) +{ + const AVTextFormatSection *section = tctx->section[tctx->level]; + const EntrySelection *selection = &selected_entries[section - sections]; + + return selection->show_all_entries || av_dict_get(selection->entries_to_show, key, NULL, 0); +} + static void log_callback(void *ptr, int level, const char *fmt, va_list vl) { AVClass* avc = ptr ? *(AVClass **) ptr : NULL; @@ -2655,14 +2670,15 @@ static int opt_format(void *optctx, const char *opt, const char *arg) static inline void mark_section_show_entries(SectionID section_id, int show_all_entries, AVDictionary *entries) { - struct AVTextFormatSection *section = §ions[section_id]; + EntrySelection *selection = &selected_entries[section_id]; - section->show_all_entries = show_all_entries; + selection->show_all_entries = show_all_entries; if (show_all_entries) { + const AVTextFormatSection *section = §ions[section_id]; for (const int *id = section->children_ids; *id != -1; id++) mark_section_show_entries(*id, show_all_entries, entries); } else { - av_dict_copy(§ion->entries_to_show, entries, 0); + av_dict_copy(&selection->entries_to_show, entries, 0); } } @@ -3053,9 +3069,12 @@ static const OptionDef real_options[] = { static inline int check_section_show_entries(int section_id) { - struct AVTextFormatSection *section = §ions[section_id]; - if (sections[section_id].show_all_entries || sections[section_id].entries_to_show) + const EntrySelection *selection = &selected_entries[section_id]; + + if (selection->show_all_entries || selection->entries_to_show) return 1; + + const AVTextFormatSection *section = §ions[section_id]; for (const int *id = section->children_ids; *id != -1; id++) if (check_section_show_entries(*id)) return 1; @@ -3074,7 +3093,7 @@ int main(int argc, char **argv) AVTextWriterContext *wctx; char *buf; char *f_name = NULL, *f_args = NULL; - int ret, input_ret, i; + int ret, input_ret; init_dynload(); @@ -3168,6 +3187,7 @@ int main(int argc, char **argv) goto end; AVTextFormatOptions tf_options = { + .is_key_selected = is_key_selected_callback, .show_optional_fields = show_optional_fields, .show_value_unit = show_value_unit, .use_value_prefix = use_value_prefix, @@ -3224,8 +3244,8 @@ end: av_freep(&read_intervals); uninit_opts(); - for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) - av_dict_free(&(sections[i].entries_to_show)); + for (size_t i = 0; i < FF_ARRAY_ELEMS(selected_entries); ++i) + av_dict_free(&selected_entries[i].entries_to_show); avformat_network_deinit(); diff --git a/fftools/graph/graphprint.c b/fftools/graph/graphprint.c index f87ccfa0e7..71dd421245 100644 --- a/fftools/graph/graphprint.c +++ b/fftools/graph/graphprint.c @@ -74,7 +74,7 @@ typedef enum { SECTION_ID_ENCODER, } SectionID; -static struct AVTextFormatSection sections[] = { +static const AVTextFormatSection sections[] = { [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER, { SECTION_ID_FILTERGRAPHS, SECTION_ID_INPUTFILES, SECTION_ID_OUTPUTFILES, SECTION_ID_DECODERS, SECTION_ID_ENCODERS, SECTION_ID_STREAMLINKS, -1 } }, [SECTION_ID_FILTERGRAPHS] = { SECTION_ID_FILTERGRAPHS, "graphs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FILTERGRAPH, -1 } }, @@ -470,12 +470,6 @@ static void print_filter(GraphPrintContext *gpc, const AVFilterContext *filter, avtext_print_section_footer(tfc); // SECTION_ID_FILTER } -static void init_sections(void) -{ - for (unsigned i = 0; i < FF_ARRAY_ELEMS(sections); i++) - sections[i].show_all_entries = 1; -} - static void print_filtergraph_single(GraphPrintContext *gpc, FilterGraph *fg, AVFilterGraph *graph) { AVTextFormatContext *tfc = gpc->tfc; @@ -877,7 +871,6 @@ static int init_graphprint(GraphPrintContext **pgpc, AVBPrint *target_buf) GraphPrintContext *gpc = NULL; int ret; - init_sections(); *pgpc = NULL; av_bprint_init(target_buf, 0, AV_BPRINT_SIZE_UNLIMITED); diff --git a/fftools/textformat/avtextformat.c b/fftools/textformat/avtextformat.c index e8e43c3c37..8a87b9ee2c 100644 --- a/fftools/textformat/avtextformat.c +++ b/fftools/textformat/avtextformat.c @@ -150,6 +150,7 @@ int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *form goto fail; } + tctx->is_key_selected = options.is_key_selected; tctx->show_value_unit = options.show_value_unit; tctx->use_value_prefix = options.use_value_prefix; tctx->use_byte_value_binary_prefix = options.use_byte_value_binary_prefix; @@ -293,8 +294,6 @@ void avtext_print_section_footer(AVTextFormatContext *tctx) void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t val, int flags) { - const AVTextFormatSection *section; - av_assert0(tctx); if (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER) @@ -307,9 +306,7 @@ void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t va 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)) { + if (!tctx->is_key_selected || tctx->is_key_selected(tctx, key)) { tctx->formatter->print_integer(tctx, key, val); tctx->nb_item[tctx->level]++; } @@ -464,7 +461,7 @@ int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char * && !(tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS)) return 0; - if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) { + if (!tctx->is_key_selected || tctx->is_key_selected(tctx, key)) { if (flags & AV_TEXTFORMAT_PRINT_STRING_VALIDATE) { char *key1 = NULL, *val1 = NULL; ret = validate_string(tctx, &key1, key); diff --git a/fftools/textformat/avtextformat.h b/fftools/textformat/avtextformat.h index cf23d93871..bd8c5d742f 100644 --- a/fftools/textformat/avtextformat.h +++ b/fftools/textformat/avtextformat.h @@ -57,9 +57,7 @@ typedef struct AVTextFormatSection { const int children_ids[SECTION_MAX_NB_CHILDREN + 1]; ///< list of children section IDS, terminated by -1 const char *element_name; ///< name of the contained element, if provided const char *unique_name; ///< unique section name, in case the name is ambiguous - AVDictionary *entries_to_show; const char *(*get_type)(const void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined - int show_all_entries; const char *id_key; ///< name of the key to be used as the id const char *src_id_key; ///< name of the key to be used as the source id for diagram connections const char *dest_id_key; ///< name of the key to be used as the target id for diagram connections @@ -131,6 +129,16 @@ struct AVTextFormatContext { AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section, /// used by various formatters + /** + * Callback to discard certain elements based upon the key used. + * It is called before any element with a key is printed. + * If this callback is unset, all elements are printed. + * + * @retval 1 if the element is supposed to be printed + * @retval 0 if the element is supposed to be discarded + */ + int (*is_key_selected)(struct AVTextFormatContext *tctx, const char *key); + int show_optional_fields; int show_value_unit; int use_value_prefix; @@ -145,6 +153,7 @@ struct AVTextFormatContext { }; typedef struct AVTextFormatOptions { + int (*is_key_selected)(struct AVTextFormatContext *tctx, const char *key); int show_optional_fields; int show_value_unit; int use_value_prefix; -- 2.45.2