[FFmpeg-devel] [PATCH v7 1/3] avfilter/graphdump: support for the graph2dot function
lance.lmwang at gmail.com
lance.lmwang at gmail.com
Sat Jun 6 02:20:29 EEST 2020
On Fri, Jun 05, 2020 at 05:06:53PM +0200, Nicolas George wrote:
> lance.lmwang at gmail.com (12020-05-25):
> > From: Limin Wang <lance.lmwang at gmail.com>
> >
> > Signed-off-by: Limin Wang <lance.lmwang at gmail.com>
> > ---
> > libavfilter/Makefile | 1 -
> > libavfilter/graphdump.c | 89 +++++++++++++++++++++
> > tools/graph2dot.c | 204 ------------------------------------------------
> > 3 files changed, 89 insertions(+), 205 deletions(-)
> > delete mode 100644 tools/graph2dot.c
>
> I think the parsing of the options string should go in a separate first
> patch. Then another separate patch to add file output. Then this patch.
OK, will split the patch.
>
> >
> > diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> > index 4d07bb6..291126e 100644
> > --- a/libavfilter/Makefile
> > +++ b/libavfilter/Makefile
> > @@ -526,7 +526,6 @@ SKIPHEADERS-$(CONFIG_VULKAN) += vulkan.h
> >
> > OBJS-$(CONFIG_LIBGLSLANG) += glslang.o
> >
> > -TOOLS = graph2dot
> > TESTPROGS = drawutils filtfmts formats integral
> >
> > TOOLS-$(CONFIG_LIBZMQ) += zmqsend
> > diff --git a/libavfilter/graphdump.c b/libavfilter/graphdump.c
> > index 79ef1a7..97d4f39 100644
> > --- a/libavfilter/graphdump.c
> > +++ b/libavfilter/graphdump.c
> > @@ -151,15 +151,104 @@ static void avfilter_graph_dump_to_buf(AVBPrint *buf, AVFilterGraph *graph)
> > }
> > }
> >
> > +static void avfilter_graph2dot_to_buf(AVBPrint *buf, AVFilterGraph *graph)
> > +{
> > + int i, j;
> > +
> > + av_bprintf(buf, "digraph G {\n");
> > + av_bprintf(buf, "node [shape=box]\n");
> > + av_bprintf(buf, "rankdir=LR\n");
> > +
> > + for (i = 0; i < graph->nb_filters; i++) {
>
> > + char filter_ctx_label[128];
> > + const AVFilterContext *filter_ctx = graph->filters[i];
> > +
> > + snprintf(filter_ctx_label, sizeof(filter_ctx_label), "%s\\n(%s)",
> > + filter_ctx->name,
> > + filter_ctx->filter->name);
>
> Do not use a temporary buffer when the target is AVBprint. Same below.
will fix
>
> > +
> > + for (j = 0; j < filter_ctx->nb_outputs; j++) {
> > + AVFilterLink *link = filter_ctx->outputs[j];
> > + if (link) {
> > + char dst_filter_ctx_label[128];
> > + const AVFilterContext *dst_filter_ctx = link->dst;
> > +
> > + snprintf(dst_filter_ctx_label, sizeof(dst_filter_ctx_label),
> > + "%s\\n(%s)",
> > + dst_filter_ctx->name,
> > + dst_filter_ctx->filter->name);
> > +
>
> > + av_bprintf(buf, "\"%s\" -> \"%s\" [ label= \"inpad:%s -> outpad:%s\\n",
> > + filter_ctx_label, dst_filter_ctx_label,
> > + avfilter_pad_get_name(link->srcpad, 0),
> > + avfilter_pad_get_name(link->dstpad, 0));
>
> Indentation is off. Same below.
will fix
>
> > +
> > + if (link->type == AVMEDIA_TYPE_VIDEO) {
> > + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
> > + av_bprintf(buf,
> > + "fmt:%s w:%d h:%d tb:%d/%d",
> > + desc->name,
> > + link->w, link->h,
> > + link->time_base.num, link->time_base.den);
> > + } else if (link->type == AVMEDIA_TYPE_AUDIO) {
> > + char audio_buf[255];
> > + av_get_channel_layout_string(audio_buf, sizeof(audio_buf), -1,
> > + link->channel_layout);
> > + av_bprintf(buf,
> > + "fmt:%s sr:%d cl:%s tb:%d/%d",
> > + av_get_sample_fmt_name(link->format),
> > + link->sample_rate, audio_buf,
> > + link->time_base.num, link->time_base.den);
> > + }
> > + av_bprintf(buf, "\" ];\n");
> > + }
> > + }
> > + }
> > + av_bprintf(buf, "}\n");
> > +}
>
> I did not check the logic itself, I assume it is identical to graph2dot.
Yes, I try to keep the same for this version and consider to add more options later.
>
> > +
> > char *avfilter_graph_dump(AVFilterGraph *graph, const char *options)
> > {
> > AVBPrint buf;
> > char *dump = NULL;
> > + int ret;
> > + AVDictionary *dict = NULL;
> > + AVDictionaryEntry *format = NULL;
> > + AVDictionaryEntry *filename = NULL;
> > +
> > + ret = av_dict_parse_string(&dict, options, "=", ":", 0);
> > + if (ret < 0) {
> > + av_dict_free(&dict);
> > + return NULL;
> > + }
> > + format = av_dict_get(dict, "fmt", NULL, 0);
> >
> > + if (format && !av_strcasecmp(format->value, "DOT")) {
> > + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
> > + avfilter_graph2dot_to_buf(&buf, graph);
>
> > + av_bprint_finalize(&buf, &dump);
>
> Missing error check. (Yes, the error check is also missing in the
> existing code.)
I prefer to add it after the patch including the code re-indention.
>
> > + } else {
>
> Invalid format selections should be reported to users.
>
> > av_bprint_init(&buf, 0, AV_BPRINT_SIZE_COUNT_ONLY);
> > avfilter_graph_dump_to_buf(&buf, graph);
> > av_bprint_init(&buf, buf.len + 1, buf.len + 1);
> > avfilter_graph_dump_to_buf(&buf, graph);
> > av_bprint_finalize(&buf, &dump);
> > + }
> > +
> > + if (filename = av_dict_get(dict, "filename", NULL, 0)) {
>
> > + FILE* file = fopen(filename->value, "w");
>
> Nit: Pointer marks belong with the variable, not the type.
so change the file to other name? I'm not clear with your comment yet.
>
> > + if (!file) {
> > + av_log(graph, AV_LOG_ERROR, "failed to open: %s \n", filename->value);
> > + av_freep(&dump);
> > + return NULL;
> > + }
>
> > + if (dump) {
>
> Urgh. If something failed, the code should have stopped long ago.
OK, will remove the check.
>
> > + fputs(dump, file);
> > + fflush(file);
> > + }
> > + fclose(file);
> > + }
> > +
>
> > + av_dict_free(&dict);
>
> Unused options should be reported to users, possibly cause an error.
OK, will add report message.
>
> > return dump;
> > }
> > diff --git a/tools/graph2dot.c b/tools/graph2dot.c
> > deleted file mode 100644
> > index d5c1e4e..0000000
> > --- a/tools/graph2dot.c
> > +++ /dev/null
> > @@ -1,204 +0,0 @@
> > -/*
>
> > - * Copyright (c) 2008-2010 Stefano Sabatini
>
> Please make sure Stefano's copyright is not lost.
the file is deleted, so I'm not clear how to keep it?
or add one more to graphdump.c?
>
> > - *
> > - * 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
> > - */
> > -
> > -#include "config.h"
> > -#if HAVE_UNISTD_H
> > -#include <unistd.h> /* getopt */
> > -#endif
> > -#include <stdio.h>
> > -#include <string.h>
> > -
> > -#include "libavutil/channel_layout.h"
> > -#include "libavutil/mem.h"
> > -#include "libavutil/pixdesc.h"
> > -#include "libavfilter/avfilter.h"
> > -
> > -#if !HAVE_GETOPT
> > -#include "compat/getopt.c"
> > -#endif
> > -
> > -static void usage(void)
> > -{
> > - printf("Convert a libavfilter graph to a dot file.\n");
> > - printf("Usage: graph2dot [OPTIONS]\n");
> > - printf("\n"
> > - "Options:\n"
> > - "-i INFILE set INFILE as input file, stdin if omitted\n"
> > - "-o OUTFILE set OUTFILE as output file, stdout if omitted\n"
> > - "-h print this help\n");
> > -}
> > -
> > -struct line {
> > - char data[256];
> > - struct line *next;
> > -};
> > -
> > -static void print_digraph(FILE *outfile, AVFilterGraph *graph)
> > -{
> > - int i, j;
> > -
> > - fprintf(outfile, "digraph G {\n");
> > - fprintf(outfile, "node [shape=box]\n");
> > - fprintf(outfile, "rankdir=LR\n");
> > -
> > - for (i = 0; i < graph->nb_filters; i++) {
> > - char filter_ctx_label[128];
> > - const AVFilterContext *filter_ctx = graph->filters[i];
> > -
> > - snprintf(filter_ctx_label, sizeof(filter_ctx_label), "%s\\n(%s)",
> > - filter_ctx->name,
> > - filter_ctx->filter->name);
> > -
> > - for (j = 0; j < filter_ctx->nb_outputs; j++) {
> > - AVFilterLink *link = filter_ctx->outputs[j];
> > - if (link) {
> > - char dst_filter_ctx_label[128];
> > - const AVFilterContext *dst_filter_ctx = link->dst;
> > -
> > - snprintf(dst_filter_ctx_label, sizeof(dst_filter_ctx_label),
> > - "%s\\n(%s)",
> > - dst_filter_ctx->name,
> > - dst_filter_ctx->filter->name);
> > -
> > - fprintf(outfile, "\"%s\" -> \"%s\" [ label= \"inpad:%s -> outpad:%s\\n",
> > - filter_ctx_label, dst_filter_ctx_label,
> > - avfilter_pad_get_name(link->srcpad, 0),
> > - avfilter_pad_get_name(link->dstpad, 0));
> > -
> > - if (link->type == AVMEDIA_TYPE_VIDEO) {
> > - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
> > - fprintf(outfile,
> > - "fmt:%s w:%d h:%d tb:%d/%d",
> > - desc->name,
> > - link->w, link->h,
> > - link->time_base.num, link->time_base.den);
> > - } else if (link->type == AVMEDIA_TYPE_AUDIO) {
> > - char buf[255];
> > - av_get_channel_layout_string(buf, sizeof(buf), -1,
> > - link->channel_layout);
> > - fprintf(outfile,
> > - "fmt:%s sr:%d cl:%s tb:%d/%d",
> > - av_get_sample_fmt_name(link->format),
> > - link->sample_rate, buf,
> > - link->time_base.num, link->time_base.den);
> > - }
> > - fprintf(outfile, "\" ];\n");
> > - }
> > - }
> > - }
> > - fprintf(outfile, "}\n");
> > -}
> > -
> > -int main(int argc, char **argv)
> > -{
> > - const char *outfilename = NULL;
> > - const char *infilename = NULL;
> > - FILE *outfile = NULL;
> > - FILE *infile = NULL;
> > - char *graph_string = NULL;
> > - AVFilterGraph *graph = av_mallocz(sizeof(AVFilterGraph));
> > - char c;
> > -
> > - av_log_set_level(AV_LOG_DEBUG);
> > -
> > - while ((c = getopt(argc, argv, "hi:o:")) != -1) {
> > - switch (c) {
> > - case 'h':
> > - usage();
> > - return 0;
> > - case 'i':
> > - infilename = optarg;
> > - break;
> > - case 'o':
> > - outfilename = optarg;
> > - break;
> > - case '?':
> > - return 1;
> > - }
> > - }
> > -
> > - if (!infilename || !strcmp(infilename, "-"))
> > - infilename = "/dev/stdin";
> > - infile = fopen(infilename, "r");
> > - if (!infile) {
> > - fprintf(stderr, "Failed to open input file '%s': %s\n",
> > - infilename, strerror(errno));
> > - return 1;
> > - }
> > -
> > - if (!outfilename || !strcmp(outfilename, "-"))
> > - outfilename = "/dev/stdout";
> > - outfile = fopen(outfilename, "w");
> > - if (!outfile) {
> > - fprintf(stderr, "Failed to open output file '%s': %s\n",
> > - outfilename, strerror(errno));
> > - return 1;
> > - }
> > -
> > - /* read from infile and put it in a buffer */
> > - {
> > - int64_t count = 0;
> > - struct line *line, *last_line, *first_line;
> > - char *p;
> > - last_line = first_line = av_malloc(sizeof(struct line));
> > - if (!last_line) {
> > - fprintf(stderr, "Memory allocation failure\n");
> > - return 1;
> > - }
> > -
> > - while (fgets(last_line->data, sizeof(last_line->data), infile)) {
> > - struct line *new_line = av_malloc(sizeof(struct line));
> > - if (!new_line) {
> > - fprintf(stderr, "Memory allocation failure\n");
> > - return 1;
> > - }
> > - count += strlen(last_line->data);
> > - last_line->next = new_line;
> > - last_line = new_line;
> > - }
> > - last_line->next = NULL;
> > -
> > - graph_string = av_malloc(count + 1);
> > - if (!graph_string) {
> > - fprintf(stderr, "Memory allocation failure\n");
> > - return 1;
> > - }
> > - p = graph_string;
> > - for (line = first_line; line->next; line = line->next) {
> > - size_t l = strlen(line->data);
> > - memcpy(p, line->data, l);
> > - p += l;
> > - }
> > - *p = '\0';
> > - }
> > -
> > - if (avfilter_graph_parse(graph, graph_string, NULL, NULL, NULL) < 0) {
> > - fprintf(stderr, "Failed to parse the graph description\n");
> > - return 1;
> > - }
> > -
> > - if (avfilter_graph_config(graph, NULL) < 0)
> > - return 1;
> > -
> > - print_digraph(outfile, graph);
> > - fflush(outfile);
> > -
> > - return 0;
> > -}
>
> Regards,
>
> --
> Nicolas George
--
Thanks,
Limin Wang
More information about the ffmpeg-devel
mailing list