[FFmpeg-devel] [PATCH v7 1/3] avfilter/graphdump: support for the graph2dot function
Nicolas George
george at nsup.org
Fri Jun 5 18:06:53 EEST 2020
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.
>
> 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.
> +
> + 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.
> +
> + 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.
> +
> 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.)
> + } 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.
> + 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.
> + fputs(dump, file);
> + fflush(file);
> + }
> + fclose(file);
> + }
> +
> + av_dict_free(&dict);
Unused options should be reported to users, possibly cause an error.
> 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.
> - *
> - * 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20200605/2f25dda3/attachment.sig>
More information about the ffmpeg-devel
mailing list