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.

462 lines
12KB

  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. enum LinkType {
  130. LinkTypeIn,
  131. LinkTypeOut,
  132. };
  133. /**
  134. * A linked-list of the inputs/outputs of the filter chain.
  135. */
  136. typedef struct AVFilterInOut {
  137. enum LinkType type;
  138. const char *name;
  139. AVFilterContext *filter;
  140. int pad_idx;
  141. struct AVFilterInOut *next;
  142. } AVFilterInOut;
  143. static void free_inout(AVFilterInOut *head)
  144. {
  145. while (head) {
  146. AVFilterInOut *next = head->next;
  147. av_free(head);
  148. head = next;
  149. }
  150. }
  151. static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
  152. {
  153. AVFilterInOut *ret;
  154. AVFilterInOut *p;
  155. if(!links || !*links)
  156. return NULL;
  157. if(!strcmp((*links)->name, label)) {
  158. ret = *links;
  159. *links = (*links)->next;
  160. return ret;
  161. }
  162. /* First check if the label is not in the openLinks list */
  163. for(p = *links; p->next && strcmp(p->next->name, label); p = p->next);
  164. if(!p->next)
  165. return NULL;
  166. ret = p->next;
  167. p->next = p->next->next;
  168. return ret;
  169. }
  170. static int link_filter_inouts(AVFilterContext *filter,
  171. AVFilterInOut **currInputs,
  172. AVFilterInOut **openLinks, AVClass *log_ctx)
  173. {
  174. AVFilterInOut *p;
  175. int pad = 0;
  176. pad = filter->input_count;
  177. while(pad) {
  178. p = *currInputs;
  179. pad--;
  180. if(!p) {
  181. av_log(log_ctx, AV_LOG_ERROR,
  182. "Not enough inputs specified for the \"%s\" filter.\n",
  183. filter->name);
  184. return -1;
  185. }
  186. if(p->filter) {
  187. if(link_filter(p->filter, p->pad_idx, filter, pad, log_ctx))
  188. return -1;
  189. *currInputs = (*currInputs)->next;
  190. av_free(p);
  191. } else {
  192. p = *currInputs;
  193. *currInputs = (*currInputs)->next;
  194. p->filter = filter;
  195. p->pad_idx = pad;
  196. p->next = *openLinks;
  197. *openLinks = p;
  198. }
  199. }
  200. if(*currInputs) {
  201. av_log(log_ctx, AV_LOG_ERROR,
  202. "Too many inputs specified for the \"%s\" filter.\n",
  203. filter->name);
  204. return -1;
  205. }
  206. pad = filter->output_count;
  207. while(pad) {
  208. AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut));
  209. pad--;
  210. currlinkn->name = NULL;
  211. currlinkn->type = LinkTypeOut;
  212. currlinkn->filter = filter;
  213. currlinkn->pad_idx = pad;
  214. currlinkn->next = *currInputs;
  215. *currInputs = currlinkn;
  216. }
  217. return 0;
  218. }
  219. /**
  220. * Parse "filter=params"
  221. * @arg name a pointer (that need to be free'd after use) to the name of the
  222. * filter
  223. * @arg ars a pointer (that need to be free'd after use) to the args of the
  224. * filter
  225. */
  226. static AVFilterContext *parse_filter(const char **buf, AVFilterGraph *graph,
  227. int index, AVClass *log_ctx)
  228. {
  229. char *opts;
  230. char *name = consume_string(buf);
  231. if(**buf == '=') {
  232. (*buf)++;
  233. opts = consume_string(buf);
  234. } else {
  235. opts = NULL;
  236. }
  237. return create_filter(graph, index, name, opts, log_ctx);
  238. }
  239. static int parse_inputs(const char **buf, AVFilterInOut **currInputs,
  240. AVFilterInOut **openLinks, AVClass *log_ctx)
  241. {
  242. int pad = 0;
  243. AVFilterInOut *p;
  244. while (**buf == '[') {
  245. char *name;
  246. parse_link_name(buf, &name, log_ctx);
  247. if(!name)
  248. return -1;
  249. /* First check if the label is not in the openLinks list */
  250. p = extract_inout(name, openLinks);
  251. /* Not in the list, so add it as an input */
  252. if(!p) {
  253. AVFilterInOut *currlinkn = av_malloc(sizeof(AVFilterInOut));
  254. currlinkn->name = name;
  255. currlinkn->type = LinkTypeIn;
  256. currlinkn->filter = NULL;
  257. currlinkn->pad_idx = pad;
  258. currlinkn->next = *currInputs;
  259. *currInputs = currlinkn;
  260. } else {
  261. /* A label of a open link. Make it one of the inputs of the next
  262. filter */
  263. AVFilterInOut *currlinkn = p;
  264. if (p->type != LinkTypeOut) {
  265. av_log(log_ctx, AV_LOG_ERROR,
  266. "Label \"%s\" appears twice as input!\n", p->name);
  267. return -1;
  268. }
  269. currlinkn->next = *currInputs;
  270. *currInputs = currlinkn;
  271. }
  272. consume_whitespace(buf);
  273. pad++;
  274. }
  275. return pad;
  276. }
  277. static int parse_outputs(const char **buf, AVFilterInOut **currInputs,
  278. AVFilterInOut **openLinks, AVClass *log_ctx)
  279. {
  280. int pad = 0;
  281. while (**buf == '[') {
  282. char *name;
  283. AVFilterInOut *match;
  284. parse_link_name(buf, &name, log_ctx);
  285. if(!name)
  286. return -1;
  287. /* First check if the label is not in the openLinks list */
  288. match = extract_inout(name, openLinks);
  289. /* Not in the list, so add the first input as a openLink */
  290. if(!match) {
  291. AVFilterInOut *p = *currInputs;
  292. *currInputs = (*currInputs)->next;
  293. p->next = *openLinks;
  294. p->type = LinkTypeOut;
  295. p->name = name;
  296. *openLinks = p;
  297. } else {
  298. /* A label of a open link. Link it. */
  299. AVFilterInOut *p = *currInputs;
  300. if (match->type != LinkTypeIn) {
  301. av_log(log_ctx, AV_LOG_ERROR,
  302. "Label \"%s\" appears twice as output!\n", match->name);
  303. return -1;
  304. }
  305. *currInputs = (*currInputs)->next;
  306. if(link_filter(p->filter, p->pad_idx,
  307. match->filter, match->pad_idx, log_ctx) < 0)
  308. return -1;
  309. av_free(match);
  310. av_free(p);
  311. }
  312. consume_whitespace(buf);
  313. pad++;
  314. }
  315. return pad;
  316. }
  317. /**
  318. * Parse a string describing a filter graph.
  319. */
  320. int avfilter_parse_graph(AVFilterGraph *graph, const char *filters,
  321. AVFilterContext *in, int inpad,
  322. AVFilterContext *out, int outpad,
  323. AVClass *log_ctx)
  324. {
  325. int index = 0;
  326. char chr = 0;
  327. int pad = 0;
  328. AVFilterInOut *currInputs=NULL;
  329. AVFilterInOut *openLinks = av_malloc(sizeof(AVFilterInOut));
  330. openLinks->name = "in";
  331. openLinks->filter = in;
  332. openLinks->type = LinkTypeOut;
  333. openLinks->pad_idx = inpad;
  334. openLinks->next = av_malloc(sizeof(AVFilterInOut));
  335. openLinks->next->name = "out";
  336. openLinks->next->filter = out;
  337. openLinks->next->type = LinkTypeIn;
  338. openLinks->next->pad_idx = outpad;
  339. openLinks->next->next = NULL;
  340. do {
  341. AVFilterContext *filter;
  342. consume_whitespace(&filters);
  343. pad = parse_inputs(&filters, &currInputs, &openLinks, log_ctx);
  344. if(pad < 0)
  345. goto fail;
  346. if(!(filter = parse_filter(&filters, graph, index, log_ctx)))
  347. goto fail;
  348. if(filter->input_count == 1 && !currInputs && !index) {
  349. // First input can be ommitted if it is "[in]"
  350. const char *tmp = "[in]";
  351. pad = parse_inputs(&tmp, &currInputs, &openLinks, log_ctx);
  352. if (pad < 0)
  353. goto fail;
  354. }
  355. if(link_filter_inouts(filter, &currInputs, &openLinks, log_ctx) < 0)
  356. goto fail;
  357. pad = parse_outputs(&filters, &currInputs, &openLinks, log_ctx);
  358. if(pad < 0)
  359. goto fail;
  360. consume_whitespace(&filters);
  361. chr = *filters++;
  362. if (chr == ';' && currInputs) {
  363. av_log(log_ctx, AV_LOG_ERROR,
  364. "Could not find a output to link when parsing \"%s\"\n",
  365. filters - 1);
  366. goto fail;
  367. }
  368. index++;
  369. } while (chr == ',' || chr == ';');
  370. if(openLinks && !strcmp(openLinks->name, "out") && currInputs) {
  371. // Last output can be ommitted if it is "[out]"
  372. const char *tmp = "[out]";
  373. if(parse_outputs(&tmp, &currInputs, &openLinks, log_ctx) < 0)
  374. goto fail;
  375. }
  376. return 0;
  377. fail:
  378. avfilter_destroy_graph(graph);
  379. free_inout(openLinks);
  380. free_inout(currInputs);
  381. return -1;
  382. }