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.

365 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. static AVFilterContext *create_filter(AVFilterGraph *ctx, int index,
  27. const char *name, const char *args,
  28. AVClass *log_ctx)
  29. {
  30. AVFilterContext *filt;
  31. AVFilter *filterdef;
  32. char inst_name[30];
  33. snprintf(inst_name, sizeof(inst_name), "Parsed filter %d", index);
  34. if(!(filterdef = avfilter_get_by_name(name))) {
  35. av_log(log_ctx, AV_LOG_ERROR,
  36. "no such filter: '%s'\n", name);
  37. return NULL;
  38. }
  39. if(!(filt = avfilter_open(filterdef, inst_name))) {
  40. av_log(log_ctx, AV_LOG_ERROR,
  41. "error creating filter '%s'\n", name);
  42. return NULL;
  43. }
  44. if(avfilter_graph_add_filter(ctx, filt) < 0)
  45. return NULL;
  46. if(avfilter_init_filter(filt, args, NULL)) {
  47. av_log(log_ctx, AV_LOG_ERROR,
  48. "error initializing filter '%s' with args '%s'\n", name, args);
  49. return NULL;
  50. }
  51. return filt;
  52. }
  53. static int link_filter(AVFilterContext *src, int srcpad,
  54. AVFilterContext *dst, int dstpad,
  55. AVClass *log_ctx)
  56. {
  57. if(avfilter_link(src, srcpad, dst, dstpad)) {
  58. av_log(log_ctx, AV_LOG_ERROR,
  59. "cannot create the link %s:%d -> %s:%d\n",
  60. src->filter->name, srcpad, dst->filter->name, dstpad);
  61. return -1;
  62. }
  63. return 0;
  64. }
  65. static void consume_whitespace(const char **buf)
  66. {
  67. *buf += strspn(*buf, " \n\t");
  68. }
  69. /**
  70. * Consumes a string from *buf.
  71. * @return a copy of the consumed string, which should be free'd after use
  72. */
  73. static char *consume_string(const char **buf)
  74. {
  75. char *out = av_malloc(strlen(*buf) + 1);
  76. char *ret = out;
  77. consume_whitespace(buf);
  78. do{
  79. char c = *(*buf)++;
  80. switch (c) {
  81. case '\\':
  82. *out++= *(*buf)++;
  83. break;
  84. case '\'':
  85. while(**buf && **buf != '\'')
  86. *out++= *(*buf)++;
  87. if(**buf) (*buf)++;
  88. break;
  89. case 0:
  90. case ']':
  91. case '[':
  92. case '=':
  93. case ',':
  94. case ';':
  95. case ' ':
  96. case '\n':
  97. *out++= 0;
  98. break;
  99. default:
  100. *out++= c;
  101. }
  102. } while(out[-1]);
  103. (*buf)--;
  104. consume_whitespace(buf);
  105. return ret;
  106. }
  107. /**
  108. * Parse "[linkname]"
  109. * @arg name a pointer (that need to be free'd after use) to the name between
  110. * parenthesis
  111. */
  112. static void parse_link_name(const char **buf, char **name, AVClass *log_ctx)
  113. {
  114. const char *start = *buf;
  115. (*buf)++;
  116. *name = consume_string(buf);
  117. if(!*name[0]) {
  118. av_log(log_ctx, AV_LOG_ERROR,
  119. "Bad (empty?) label found in the following: \"%s\".\n", start);
  120. goto fail;
  121. }
  122. if(*(*buf)++ != ']') {
  123. av_log(log_ctx, AV_LOG_ERROR,
  124. "Mismatched '[' found in the following: \"%s\".\n", start);
  125. fail:
  126. av_freep(name);
  127. }
  128. }
  129. /**
  130. * Parse "filter=params"
  131. * @arg name a pointer (that need to be free'd after use) to the name of the
  132. * filter
  133. * @arg ars a pointer (that need to be free'd after use) to the args of the
  134. * filter
  135. */
  136. static AVFilterContext *parse_filter(const char **buf,
  137. AVFilterGraph *graph, int index,
  138. AVClass *log_ctx)
  139. {
  140. char *name, *opts;
  141. name = consume_string(buf);
  142. if(**buf == '=') {
  143. (*buf)++;
  144. opts = consume_string(buf);
  145. } else {
  146. opts = NULL;
  147. }
  148. return create_filter(graph, index, name, opts, log_ctx);
  149. }
  150. enum LinkType {
  151. LinkTypeIn,
  152. LinkTypeOut,
  153. };
  154. /**
  155. * A linked-list of the inputs/outputs of the filter chain.
  156. */
  157. typedef struct AVFilterInOut {
  158. enum LinkType type;
  159. char *name;
  160. AVFilterContext *filter;
  161. int pad_idx;
  162. struct AVFilterInOut *next;
  163. } AVFilterInOut;
  164. static void free_inout(AVFilterInOut *head)
  165. {
  166. while (head) {
  167. AVFilterInOut *next = head->next;
  168. av_free(head);
  169. head = next;
  170. }
  171. }
  172. /**
  173. * Parse "[a1][link2] ... [etc]"
  174. */
  175. static int parse_inouts(const char **buf, AVFilterInOut **inout, int pad,
  176. enum LinkType type, AVFilterContext *filter,
  177. AVClass *log_ctx)
  178. {
  179. while (**buf == '[') {
  180. char *name;
  181. AVFilterInOut *p = *inout;
  182. parse_link_name(buf, &name, log_ctx);
  183. if(!name)
  184. return -1;
  185. for (; p && strcmp(p->name, name); p = p->next);
  186. if(!p) {
  187. // First label apearence, add it to the linked list
  188. AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut));
  189. inoutn->name = name;
  190. inoutn->type = type;
  191. inoutn->filter = filter;
  192. inoutn->pad_idx = pad;
  193. inoutn->next = *inout;
  194. *inout = inoutn;
  195. } else {
  196. if(p->type == LinkTypeIn && type == LinkTypeOut) {
  197. if(link_filter(filter, pad, p->filter, p->pad_idx, log_ctx) < 0)
  198. return -1;
  199. } else if(p->type == LinkTypeOut && type == LinkTypeIn) {
  200. if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx) < 0)
  201. return -1;
  202. } else {
  203. av_log(log_ctx, AV_LOG_ERROR,
  204. "Two links named '%s' are either both input or both output\n",
  205. name);
  206. return -1;
  207. }
  208. p->filter = NULL;
  209. }
  210. pad++;
  211. consume_whitespace(buf);
  212. }
  213. return pad;
  214. }
  215. static const char *skip_inouts(const char *buf)
  216. {
  217. while (*buf == '[') {
  218. buf += strcspn(buf, "]") + 1;
  219. consume_whitespace(&buf);
  220. }
  221. return buf;
  222. }
  223. /**
  224. * Parse a string describing a filter graph.
  225. */
  226. int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
  227. AVFilterContext *in, int inpad,
  228. AVFilterContext *out, int outpad,
  229. AVClass *log_ctx)
  230. {
  231. AVFilterInOut *inout=NULL;
  232. AVFilterInOut *head=NULL;
  233. int index = 0;
  234. char chr = 0;
  235. int pad = 0;
  236. int has_out = 0;
  237. AVFilterContext *last_filt = NULL;
  238. do {
  239. AVFilterContext *filter;
  240. int oldpad = pad;
  241. const char *inouts;
  242. consume_whitespace(&filters);
  243. inouts = filters;
  244. // We need to parse the inputs of the filter after we create it, so
  245. // skip it by now
  246. filters = skip_inouts(filters);
  247. if(!(filter = parse_filter(&filters, graph, index, log_ctx)))
  248. goto fail;
  249. pad = parse_inouts(&inouts, &inout, chr == ',', LinkTypeIn, filter,
  250. log_ctx);
  251. if(pad < 0)
  252. goto fail;
  253. // If the first filter has an input and none was given, it is
  254. // implicitly the input of the whole graph.
  255. if(pad == 0 && filter->input_count == 1) {
  256. if(link_filter(in, inpad, filter, 0, log_ctx))
  257. goto fail;
  258. }
  259. if(chr == ',') {
  260. if(link_filter(last_filt, oldpad, filter, 0, log_ctx) < 0)
  261. goto fail;
  262. }
  263. pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, filter, log_ctx);
  264. if (pad < 0)
  265. goto fail;
  266. consume_whitespace(&filters);
  267. chr = *filters++;
  268. index++;
  269. last_filt = filter;
  270. } while (chr == ',' || chr == ';');
  271. head = inout;
  272. // Process remaining labels. Only inputs and outputs should be left.
  273. for (; inout; inout = inout->next) {
  274. if(!inout->filter)
  275. continue; // Already processed
  276. if(!strcmp(inout->name, "in")) {
  277. if(link_filter(in, inpad, inout->filter, inout->pad_idx, log_ctx))
  278. goto fail;
  279. } else if(!strcmp(inout->name, "out")) {
  280. has_out = 1;
  281. if(link_filter(inout->filter, inout->pad_idx, out, outpad, log_ctx))
  282. goto fail;
  283. } else {
  284. av_log(log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n",
  285. inout->name);
  286. goto fail;
  287. }
  288. }
  289. free_inout(head);
  290. if(!has_out) {
  291. if(link_filter(last_filt, pad, out, outpad, log_ctx))
  292. goto fail;
  293. }
  294. return 0;
  295. fail:
  296. free_inout(head);
  297. avfilter_destroy_graph(graph);
  298. return -1;
  299. }