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.

397 lines
10KB

  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 int create_filter(AVFilterGraph *ctx, int index, char *name,
  39. char *args)
  40. {
  41. AVFilterContext *filt;
  42. AVFilter *filterdef;
  43. char tmp[20];
  44. snprintf(tmp, 20, "%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 -1;
  49. }
  50. if(!(filt = avfilter_open(filterdef, tmp))) {
  51. av_log(&log_ctx, AV_LOG_ERROR,
  52. "error creating filter '%s'\n", name);
  53. return -1;
  54. }
  55. if (avfilter_graph_add_filter(ctx, filt) < 0)
  56. return -1;
  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 -1;
  61. }
  62. return 0;
  63. }
  64. static int link_filter(AVFilterGraph *ctx, int src, int srcpad,
  65. int dst, int dstpad)
  66. {
  67. AVFilterContext *filt, *filtb;
  68. char tmp[20];
  69. snprintf(tmp, 20, "%d", src);
  70. if(!(filt = avfilter_graph_get_filter(ctx, tmp))) {
  71. av_log(&log_ctx, AV_LOG_ERROR, "link source does not exist in graph\n");
  72. return -1;
  73. }
  74. snprintf(tmp, 20, "%d", dst);
  75. if(!(filtb = avfilter_graph_get_filter(ctx, tmp))) {
  76. av_log(&log_ctx, AV_LOG_ERROR, "link destination does not exist in graph\n");
  77. return -1;
  78. }
  79. if(avfilter_link(filt, srcpad, filtb, dstpad)) {
  80. av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n");
  81. return -1;
  82. }
  83. return 0;
  84. }
  85. static void consume_whitespace(const char **buf)
  86. {
  87. *buf += strspn(*buf, " \n\t");
  88. }
  89. /**
  90. * Copy the first size bytes of input string to a null-terminated string,
  91. * removing any control character. Ex: "aaa'bb'c\'c\\" -> "aaabbc'c\"
  92. */
  93. static void copy_unquoted(char *out, const char *in, int size)
  94. {
  95. int i;
  96. for (i=0; i < size; i++) {
  97. if (in[i] == '\'')
  98. continue;
  99. else if (in[i] == '\\') {
  100. if (i+1 == size) {
  101. *out = 0;
  102. return;
  103. }
  104. i++;
  105. }
  106. *out++ = in[i];
  107. }
  108. *out=0;
  109. }
  110. /**
  111. * Consumes a string from *buf.
  112. * @return a copy of the consumed string, which should be free'd after use
  113. */
  114. static char *consume_string(const char **buf)
  115. {
  116. const char *start;
  117. char *ret;
  118. int size;
  119. consume_whitespace(buf);
  120. if (!(**buf))
  121. return av_mallocz(1);
  122. start = *buf;
  123. while(1) {
  124. *buf += strcspn(*buf, " ()=,'\\");
  125. if (**buf == '\\')
  126. *buf+=2;
  127. else
  128. break;
  129. }
  130. if (**buf == '\'') {
  131. const char *p = *buf;
  132. do {
  133. p++;
  134. p = strchr(p, '\'');
  135. } while (p && p[-1] == '\\');
  136. if (p)
  137. *buf = p + 1;
  138. else
  139. *buf += strlen(*buf); // Move the pointer to the null end byte
  140. }
  141. size = *buf - start + 1;
  142. ret = av_malloc(size);
  143. copy_unquoted(ret, start, size-1);
  144. return ret;
  145. }
  146. /**
  147. * Parse "(linkname)"
  148. * @arg name a pointer (that need to be free'd after use) to the name between
  149. * parenthesis
  150. */
  151. static void parse_link_name(const char **buf, char **name)
  152. {
  153. (*buf)++;
  154. *name = consume_string(buf);
  155. if (!*name[0])
  156. goto fail;
  157. if (*(*buf)++ != ')')
  158. goto fail;
  159. return;
  160. fail:
  161. av_freep(name);
  162. av_log(&log_ctx, AV_LOG_ERROR, "Could not parse link name!\n");
  163. }
  164. /**
  165. * Parse "filter=params"
  166. * @arg name a pointer (that need to be free'd after use) to the name of the
  167. * filter
  168. * @arg ars a pointer (that need to be free'd after use) to the args of the
  169. * filter
  170. */
  171. static int parse_filter(const char **buf, AVFilterGraph *graph, int index)
  172. {
  173. char *name, *opts;
  174. name = consume_string(buf);
  175. if (**buf == '=') {
  176. (*buf)++;
  177. opts = consume_string(buf);
  178. } else {
  179. opts = NULL;
  180. }
  181. return create_filter(graph, index, name, opts);
  182. }
  183. enum LinkType {
  184. LinkTypeIn,
  185. LinkTypeOut,
  186. };
  187. /**
  188. * A linked-list of the inputs/outputs of the filter chain.
  189. */
  190. typedef struct AVFilterInOut {
  191. enum LinkType type;
  192. char *name;
  193. int instance;
  194. int pad_idx;
  195. struct AVFilterInOut *next;
  196. } AVFilterInOut;
  197. static void free_inout(AVFilterInOut *head)
  198. {
  199. while (head) {
  200. AVFilterInOut *next;
  201. next = head->next;
  202. av_free(head);
  203. head = next;
  204. }
  205. }
  206. /**
  207. * Parse "(a1)(link2) ... (etc)"
  208. */
  209. static int parse_inouts(const char **buf, AVFilterInOut **inout, int firstpad,
  210. enum LinkType type, int instance)
  211. {
  212. int pad = firstpad;
  213. while (**buf == '(') {
  214. AVFilterInOut *inoutn = av_malloc(sizeof(AVFilterInOut));
  215. parse_link_name(buf, &inoutn->name);
  216. inoutn->type = type;
  217. inoutn->instance = instance;
  218. inoutn->pad_idx = pad++;
  219. inoutn->next = *inout;
  220. *inout = inoutn;
  221. }
  222. return pad;
  223. }
  224. /**
  225. * Parse a string describing a filter graph.
  226. */
  227. int avfilter_graph_parse_chain(AVFilterGraph *graph, const char *filters, AVFilterContext *in, int inpad, AVFilterContext *out, int outpad)
  228. {
  229. AVFilterInOut *inout=NULL;
  230. AVFilterInOut *head=NULL;
  231. int index = 0;
  232. char chr = 0;
  233. int pad = 0;
  234. int has_out = 0;
  235. char tmp[20];
  236. AVFilterContext *filt;
  237. consume_whitespace(&filters);
  238. do {
  239. int oldpad = pad;
  240. pad = parse_inouts(&filters, &inout, chr == ',', LinkTypeIn, index);
  241. if (parse_filter(&filters, graph, index) < 0)
  242. goto fail;
  243. // If the first filter has an input and none was given, it is
  244. // implicitly the input of the whole graph.
  245. if (pad == 0 && graph->filters[graph->filter_count-1]->input_count == 1) {
  246. snprintf(tmp, 20, "%d", index);
  247. if(!(filt = avfilter_graph_get_filter(graph, tmp))) {
  248. av_log(&log_ctx, AV_LOG_ERROR, "filter owning exported pad does not exist\n");
  249. goto fail;
  250. }
  251. if(avfilter_link(in, inpad, filt, 0)) {
  252. av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n");
  253. goto fail;
  254. }
  255. }
  256. if(chr == ',') {
  257. if (link_filter(graph, index-1, oldpad, index, 0) < 0)
  258. goto fail;
  259. }
  260. pad = parse_inouts(&filters, &inout, 0, LinkTypeOut, index);
  261. chr = *filters++;
  262. index++;
  263. } while (chr == ',' || chr == ';');
  264. head = inout;
  265. for (; inout != NULL; inout = inout->next) {
  266. if (inout->instance == -1)
  267. continue; // Already processed
  268. if (!strcmp(inout->name, "in")) {
  269. snprintf(tmp, 20, "%d", inout->instance);
  270. if(!(filt = avfilter_graph_get_filter(graph, tmp))) {
  271. av_log(&log_ctx, AV_LOG_ERROR, "filter owning exported pad does not exist\n");
  272. goto fail;
  273. }
  274. if(avfilter_link(in, inpad, filt, inout->pad_idx)) {
  275. av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n");
  276. goto fail;
  277. }
  278. } else if (!strcmp(inout->name, "out")) {
  279. has_out = 1;
  280. snprintf(tmp, 20, "%d", inout->instance);
  281. if(!(filt = avfilter_graph_get_filter(graph, tmp))) {
  282. av_log(&log_ctx, AV_LOG_ERROR, "filter owning exported pad does not exist\n");
  283. goto fail;
  284. }
  285. if(avfilter_link(filt, inout->pad_idx, out, outpad)) {
  286. av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n");
  287. goto fail;
  288. }
  289. } else {
  290. AVFilterInOut *p, *src, *dst;
  291. for (p = inout->next;
  292. p && strcmp(p->name,inout->name); p = p->next);
  293. if (!p) {
  294. av_log(&log_ctx, AV_LOG_ERROR, "Unmatched link: %s.\n",
  295. inout->name);
  296. goto fail;
  297. }
  298. if (p->type == LinkTypeIn && inout->type == LinkTypeOut) {
  299. src = inout;
  300. dst = p;
  301. } else if (p->type == LinkTypeOut && inout->type == LinkTypeIn) {
  302. src = p;
  303. dst = inout;
  304. } else {
  305. av_log(&log_ctx, AV_LOG_ERROR, "Two links named '%s' are either both input or both output\n",
  306. inout->name);
  307. goto fail;
  308. }
  309. if (link_filter(graph, src->instance, src->pad_idx, dst->instance, dst->pad_idx) < 0)
  310. goto fail;
  311. src->instance = -1;
  312. dst->instance = -1;
  313. }
  314. }
  315. free_inout(head);
  316. if (!has_out) {
  317. snprintf(tmp, 20, "%d", index-1);
  318. if(!(filt = avfilter_graph_get_filter(graph, tmp))) {
  319. av_log(&log_ctx, AV_LOG_ERROR, "filter owning exported pad does not exist\n");
  320. goto fail;
  321. }
  322. if(avfilter_link(filt, pad, out, outpad)) {
  323. av_log(&log_ctx, AV_LOG_ERROR, "cannot create link between source and destination filters\n");
  324. goto fail;
  325. }
  326. }
  327. return 0;
  328. fail:
  329. free_inout(head);
  330. avfilter_destroy_graph(graph);
  331. return -1;
  332. }