You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
9.1KB

  1. /*
  2. * filter graph parser
  3. * copyright (c) 2008 Vitor Sessak
  4. * copyright (c) 2007 Bobby Bingham
  5. *
  6. * This file is part of FFmpeg.
  7. *
  8. * FFmpeg is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * FFmpeg is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with FFmpeg; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. #include <ctype.h>
  23. #include <string.h>
  24. #include "avfilter.h"
  25. #include "avfiltergraph.h"
  26. /**
  27. * For use in av_log
  28. */
  29. static const char *log_name(void *p)
  30. {
  31. return "Filter parser";
  32. }
  33. static const AVClass filter_parser_class = {
  34. "Filter parser",
  35. log_name
  36. };
  37. static const AVClass *log_ctx = &filter_parser_class;
  38. static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
  39. const char *name, const char *args)
  40. {
  41. AVFilterContext *filt;
  42. AVFilter *filterdef;
  43. char inst_name[30];
  44. snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
  45. if(!(filterdef = avfilter_get_by_name(name))) {
  46. av_log(&log_ctx, AV_LOG_ERROR,
  47. "no such filter: '%s'\n", name);
  48. return NULL;
  49. }
  50. if(!(filt = avfilter_open(filterdef, inst_name))) {
  51. av_log(&log_ctx, AV_LOG_ERROR,
  52. "error creating filter '%s'\n", name);
  53. return NULL;
  54. }
  55. if(avfilter_graph_add_filter(ctx, filt) < 0)
  56. return NULL;
  57. if(avfilter_init_filter(filt, args, NULL)) {
  58. av_log(&log_ctx, AV_LOG_ERROR,
  59. "error initializing filter '%s' with args '%s'\n", name, args);
  60. return NULL;
  61. }
  62. return filt;
  63. }
  64. static int link_filter(AVFilterContext *src, int srcpad,
  65. AVFilterContext *dst, int dstpad)
  66. {
  67. if(avfilter_link(src, srcpad, dst, dstpad)) {
  68. av_log(&log_ctx, AV_LOG_ERROR,
  69. "cannot create the link %s:%d -> %s:%d\n",
  70. src->filter->name, srcpad, dst->filter->name, dstpad);
  71. return -1;
  72. }
  73. return 0;
  74. }
  75. static void consume_whitespace(const char **buf)
  76. {
  77. *buf += strspn(*buf, " \n\t");
  78. }
  79. /**
  80. * Consumes a string from *buf.
  81. * @return a copy of the consumed string, which should be free'd after use
  82. */
  83. static char *consume_string(const char **buf)
  84. {
  85. char *out = av_malloc(strlen(*buf) + 1);
  86. char *ret = out;
  87. consume_whitespace(buf);
  88. do{
  89. char c = *(*buf)++;
  90. switch (c) {
  91. case '\\':
  92. *out++= *(*buf)++;
  93. break;
  94. case '\'':
  95. while(**buf && **buf != '\'')
  96. *out++= *(*buf)++;
  97. if(**buf) (*buf)++;
  98. break;
  99. case 0:
  100. case ']':
  101. case '[':
  102. case '=':
  103. case ',':
  104. case ' ':
  105. case '\n':
  106. *out++= 0;
  107. break;
  108. default:
  109. *out++= c;
  110. }
  111. } while(out[-1]);
  112. (*buf)--;
  113. consume_whitespace(buf);
  114. return ret;
  115. }
  116. /**
  117. * Parse "[linkname]"
  118. * @arg name a pointer (that need to be free'd after use) to the name between
  119. * parenthesis
  120. */
  121. static void parse_link_name(const char **buf, char **name)
  122. {
  123. const char *start = *buf;
  124. (*buf)++;
  125. *name = consume_string(buf);
  126. if(!*name[0]) {
  127. av_log(&log_ctx, AV_LOG_ERROR,
  128. "Bad (empty?) label found in the following: \"%s\".\n", start);
  129. goto fail;
  130. }
  131. if(*(*buf)++ != ']') {
  132. av_log(&log_ctx, AV_LOG_ERROR,
  133. "Mismatched '[' found in the following: \"%s\".\n", start);
  134. fail:
  135. av_freep(name);
  136. }
  137. }
  138. /**
  139. * Parse "filter=params"
  140. * @arg name a pointer (that need to be free'd after use) to the name of the
  141. * filter
  142. * @arg ars a pointer (that need to be free'd after use) to the args of the
  143. * filter
  144. */
  145. static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph, int index)
  146. {
  147. char *name, *opts;
  148. name = consume_string(buf);
  149. if(**buf == '=') {
  150. (*buf)++;
  151. opts = consume_string(buf);
  152. } else {
  153. opts = NULL;
  154. }
  155. return create_filter(graph, index, name, opts);
  156. }
  157. enum LinkType {
  158. LinkTypeIn,
  159. LinkTypeOut,
  160. };
  161. /**
  162. * A linked-list of the inputs/outputs of the filter chain.
  163. */
  164. typedef struct AVFilterInOut {
  165. enum LinkType type;
  166. char *name;
  167. AVFilterContext *filter;
  168. int pad_idx;
  169. struct AVFilterInOut *next;
  170. } AVFilterInOut;
  171. static void free_inout(AVFilterInOut *head)
  172. {
  173. while (head) {
  174. AVFilterInOut *next = head->next;
  175. av_free(head);
  176. head = next;
  177. }
  178. }
  179. /**
  180. * Process a link. This funcion looks for a matching label in the *inout
  181. * linked list. If none is found, it adds this link to the list.
  182. */
  183. static int handle_link(char *name, AVFilterInOut **inout, int pad,
  184. enum LinkType type, AVFilterContext *filter)
  185. {
  186. AVFilterInOut *p = *inout;
  187. for (; p && strcmp(p->name, name); p = p->next);
  188. if(!p) {
  189. // First label apearence, add it to the linked list
  190. AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut));
  191. inoutn->name = name;
  192. inoutn->type = type;
  193. inoutn->filter = filter;
  194. inoutn->pad_idx = pad;
  195. inoutn->next = *inout;
  196. *inout = inoutn;
  197. return 0;
  198. }
  199. if(p->type == LinkTypeIn && type == LinkTypeOut) {
  200. if(link_filter(filter, pad, p->filter, p->pad_idx) < 0)
  201. goto fail;
  202. } else if(p->type == LinkTypeOut && type == LinkTypeIn) {
  203. if(link_filter(p->filter, p->pad_idx, filter, pad) < 0)
  204. goto fail;
  205. } else {
  206. av_log(&log_ctx, AV_LOG_ERROR,
  207. "Two links named '%s' are either both input or both output\n",
  208. name);
  209. goto fail;
  210. }
  211. p->filter = NULL;
  212. return 0;
  213. fail:
  214. return -1;
  215. }
  216. /**
  217. * Parse "[a1][link2] ... [etc]"
  218. */
  219. static int parse_inouts(const char **buf, AVFilterInOut **inout, int pad,
  220. enum LinkType type, AVFilterContext *filter)
  221. {
  222. while (**buf == '[') {
  223. char *name;
  224. parse_link_name(buf, &name);
  225. if(!name)
  226. return -1;
  227. handle_link(name, inout, pad++, type, filter);
  228. consume_whitespace(buf);
  229. }
  230. return pad;
  231. }
  232. static const char *skip_inouts(const char *buf)
  233. {
  234. while (*buf == '[') {
  235. buf += strcspn(buf, "]") + 1;
  236. consume_whitespace(&buf);
  237. }
  238. return buf;
  239. }
  240. /**
  241. * Parse a string describing a filter graph.
  242. */
  243. int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
  244. AVFilterContext *in, int inpad,
  245. AVFilterContext *out, int outpad)
  246. {
  247. AVFilterInOut *inout=NULL;
  248. AVFilterInOut *head=NULL;
  249. int index = 0;
  250. char chr = 0;
  251. int pad = 0;
  252. int has_out = 0;
  253. AVFilterContext *last_filt = NULL;
  254. do {
  255. AVFilterContext *filter;
  256. int oldpad = pad;
  257. const char *inouts;
  258. consume_whitespace(&filters);
  259. inouts = filters;
  260. // We need to parse the inputs of the filter after we create it, so
  261. // skip it by now
  262. filters = skip_inouts(filters);
  263. if(!(filter = parse_filter(&filters, graph, index)))
  264. goto fail;
  265. pad = parse_inouts(&inouts, &inout, chr == ',', LinkTypeIn, filter);
  266. if(pad < 0)
  267. goto fail;
  268. // If the first filter has an input and none was given, it is
  269. // implicitly the input of the whole graph.
  270. if(pad == 0 && filter->input_count == 1) {
  271. if(link_filter(in, inpad, filter, 0))
  272. goto fail;
  273. }
  274. if(chr == ',') {
  275. if(link_filter(last_filt, oldpad, filter, 0) < 0)
  276. goto fail;
  277. }
  278. pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, filter);
  279. if(pad < 0)
  280. goto fail;
  281. consume_whitespace(&filters);
  282. chr = *filters++;
  283. index++;
  284. last_filt = filter;
  285. } while (chr == ',' || chr == ';');
  286. head = inout;
  287. // Process remaining labels. Only inputs and outputs should be left.
  288. for (; inout; inout = inout->next) {
  289. if(!inout->filter)
  290. continue; // Already processed
  291. if(!strcmp(inout->name, "in")) {
  292. if(link_filter(in, inpad, inout->filter, inout->pad_idx))
  293. goto fail;
  294. } else if(!strcmp(inout->name, "out")) {
  295. has_out = 1;
  296. if(link_filter(inout->filter, inout->pad_idx, out, outpad))
  297. goto fail;
  298. } else {
  299. av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n",
  300. inout->name);
  301. goto fail;
  302. }
  303. }
  304. free_inout(head);
  305. if(!has_out) {
  306. if(link_filter(last_filt, pad, out, outpad))
  307. goto fail;
  308. }
  309. return 0;
  310. fail:
  311. free_inout(head);
  312. avfilter_destroy_graph(graph);
  313. return -1;
  314. }