Message ID | 1590364226-11567-1-git-send-email-lance.lmwang@gmail.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,v7,1/3] avfilter/graphdump: support for the graph2dot function | expand |
Context | Check | Description |
---|---|---|
andriy/default | pending | |
andriy/make | success | Make finished |
andriy/make_fate | success | Make fate finished |
On Mon, May 25, 2020 at 07:50:24AM +0800, lance.lmwang@gmail.com wrote: > From: Limin Wang <lance.lmwang@gmail.com> > > Signed-off-by: Limin Wang <lance.lmwang@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 > > 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); > + > + 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)); > + > + 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"); > +} > + > 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); > + } else { > 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"); > + if (!file) { > + av_log(graph, AV_LOG_ERROR, "failed to open: %s \n", filename->value); > + av_freep(&dump); > + return NULL; > + } > + if (dump) { > + fputs(dump, file); > + fflush(file); > + } > + fclose(file); > + } > + > + av_dict_free(&dict); > 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 > - * > - * 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; > -} > -- > 2.6.4 > will apply the patchset tomorrow if no further comments.
lance.lmwang@gmail.com (12020-05-28):
> will apply the patchset tomorrow if no further comments.
Please give me time to look carefully. This is not urgent.
Regards,
On Thu, May 28, 2020 at 04:14:27PM +0200, Nicolas George wrote: > lance.lmwang@gmail.com (12020-05-28): > > will apply the patchset tomorrow if no further comments. > > Please give me time to look carefully. This is not urgent. Of course, thanks for review, anytime is OK, stay tune. > > Regards, > > -- > Nicolas George > _______________________________________________ > 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".
On Thu, May 28, 2020 at 04:14:27PM +0200, Nicolas George wrote: > lance.lmwang@gmail.com (12020-05-28): > > will apply the patchset tomorrow if no further comments. > > Please give me time to look carefully. This is not urgent. Nicolas, do you have got free time to review the patch set and give your feedback? > > Regards, > > -- > Nicolas George > _______________________________________________ > 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".
lance.lmwang@gmail.com (12020-05-25): > From: Limin Wang <lance.lmwang@gmail.com> > > Signed-off-by: Limin Wang <lance.lmwang@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,
On Fri, Jun 05, 2020 at 05:06:53PM +0200, Nicolas George wrote: > lance.lmwang@gmail.com (12020-05-25): > > From: Limin Wang <lance.lmwang@gmail.com> > > > > Signed-off-by: Limin Wang <lance.lmwang@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
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); + + 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)); + + 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"); +} + 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); + } else { 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"); + if (!file) { + av_log(graph, AV_LOG_ERROR, "failed to open: %s \n", filename->value); + av_freep(&dump); + return NULL; + } + if (dump) { + fputs(dump, file); + fflush(file); + } + fclose(file); + } + + av_dict_free(&dict); 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 - * - * 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; -}