1/* 2 * Filter graphs to bad ASCII-art 3 * Copyright (c) 2012 Nicolas George 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include <string.h> 23 24#include "libavutil/channel_layout.h" 25#include "libavutil/bprint.h" 26#include "libavutil/pixdesc.h" 27#include "avfilter.h" 28#include "avfiltergraph.h" 29 30static int print_link_prop(AVBPrint *buf, AVFilterLink *link) 31{ 32 char *format; 33 char layout[64]; 34 AVBPrint dummy_buffer = { 0 }; 35 36 if (!buf) 37 buf = &dummy_buffer; 38 switch (link->type) { 39 case AVMEDIA_TYPE_VIDEO: 40 format = av_x_if_null(av_get_pix_fmt_name(link->format), "?"); 41 av_bprintf(buf, "[%dx%d %d:%d %s]", link->w, link->h, 42 link->sample_aspect_ratio.num, 43 link->sample_aspect_ratio.den, 44 format); 45 break; 46 47 case AVMEDIA_TYPE_AUDIO: 48 av_get_channel_layout_string(layout, sizeof(layout), 49 link->channels, link->channel_layout); 50 format = av_x_if_null(av_get_sample_fmt_name(link->format), "?"); 51 av_bprintf(buf, "[%dHz %s:%s]", 52 (int)link->sample_rate, format, layout); 53 break; 54 55 default: 56 av_bprintf(buf, "?"); 57 break; 58 } 59 return buf->len; 60} 61 62static void avfilter_graph_dump_to_buf(AVBPrint *buf, AVFilterGraph *graph) 63{ 64 unsigned i, j, x, e; 65 66 for (i = 0; i < graph->nb_filters; i++) { 67 AVFilterContext *filter = graph->filters[i]; 68 unsigned max_src_name = 0, max_dst_name = 0; 69 unsigned max_in_name = 0, max_out_name = 0; 70 unsigned max_in_fmt = 0, max_out_fmt = 0; 71 unsigned width, height, in_indent; 72 unsigned lname = strlen(filter->name); 73 unsigned ltype = strlen(filter->filter->name); 74 75 for (j = 0; j < filter->nb_inputs; j++) { 76 AVFilterLink *l = filter->inputs[j]; 77 unsigned ln = strlen(l->src->name) + 1 + strlen(l->srcpad->name); 78 max_src_name = FFMAX(max_src_name, ln); 79 max_in_name = FFMAX(max_in_name, strlen(l->dstpad->name)); 80 max_in_fmt = FFMAX(max_in_fmt, print_link_prop(NULL, l)); 81 } 82 for (j = 0; j < filter->nb_outputs; j++) { 83 AVFilterLink *l = filter->outputs[j]; 84 unsigned ln = strlen(l->dst->name) + 1 + strlen(l->dstpad->name); 85 max_dst_name = FFMAX(max_dst_name, ln); 86 max_out_name = FFMAX(max_out_name, strlen(l->srcpad->name)); 87 max_out_fmt = FFMAX(max_out_fmt, print_link_prop(NULL, l)); 88 } 89 in_indent = max_src_name + max_in_name + max_in_fmt; 90 in_indent += in_indent ? 4 : 0; 91 width = FFMAX(lname + 2, ltype + 4); 92 height = FFMAX3(2, filter->nb_inputs, filter->nb_outputs); 93 av_bprint_chars(buf, ' ', in_indent); 94 av_bprintf(buf, "+"); 95 av_bprint_chars(buf, '-', width); 96 av_bprintf(buf, "+\n"); 97 for (j = 0; j < height; j++) { 98 unsigned in_no = j - (height - filter->nb_inputs ) / 2; 99 unsigned out_no = j - (height - filter->nb_outputs) / 2; 100 101 /* Input link */ 102 if (in_no < filter->nb_inputs) { 103 AVFilterLink *l = filter->inputs[in_no]; 104 e = buf->len + max_src_name + 2; 105 av_bprintf(buf, "%s:%s", l->src->name, l->srcpad->name); 106 av_bprint_chars(buf, '-', e - buf->len); 107 e = buf->len + max_in_fmt + 2 + 108 max_in_name - strlen(l->dstpad->name); 109 print_link_prop(buf, l); 110 av_bprint_chars(buf, '-', e - buf->len); 111 av_bprintf(buf, "%s", l->dstpad->name); 112 } else { 113 av_bprint_chars(buf, ' ', in_indent); 114 } 115 116 /* Filter */ 117 av_bprintf(buf, "|"); 118 if (j == (height - 2) / 2) { 119 x = (width - lname) / 2; 120 av_bprintf(buf, "%*s%-*s", x, "", width - x, filter->name); 121 } else if (j == (height - 2) / 2 + 1) { 122 x = (width - ltype - 2) / 2; 123 av_bprintf(buf, "%*s(%s)%*s", x, "", filter->filter->name, 124 width - ltype - 2 - x, ""); 125 } else { 126 av_bprint_chars(buf, ' ', width); 127 } 128 av_bprintf(buf, "|"); 129 130 /* Output link */ 131 if (out_no < filter->nb_outputs) { 132 AVFilterLink *l = filter->outputs[out_no]; 133 unsigned ln = strlen(l->dst->name) + 1 + 134 strlen(l->dstpad->name); 135 e = buf->len + max_out_name + 2; 136 av_bprintf(buf, "%s", l->srcpad->name); 137 av_bprint_chars(buf, '-', e - buf->len); 138 e = buf->len + max_out_fmt + 2 + 139 max_dst_name - ln; 140 print_link_prop(buf, l); 141 av_bprint_chars(buf, '-', e - buf->len); 142 av_bprintf(buf, "%s:%s", l->dst->name, l->dstpad->name); 143 } 144 av_bprintf(buf, "\n"); 145 } 146 av_bprint_chars(buf, ' ', in_indent); 147 av_bprintf(buf, "+"); 148 av_bprint_chars(buf, '-', width); 149 av_bprintf(buf, "+\n"); 150 av_bprintf(buf, "\n"); 151 } 152} 153 154char *avfilter_graph_dump(AVFilterGraph *graph, const char *options) 155{ 156 AVBPrint buf; 157 char *dump; 158 159 av_bprint_init(&buf, 0, 0); 160 avfilter_graph_dump_to_buf(&buf, graph); 161 av_bprint_init(&buf, buf.len + 1, buf.len + 1); 162 avfilter_graph_dump_to_buf(&buf, graph); 163 av_bprint_finalize(&buf, &dump); 164 return dump; 165} 166