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.

543 lines
15KB

  1. /*
  2. * Filter graphs
  3. * copyright (c) 2007 Bobby Bingham
  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. #include <string.h>
  22. #include <stddef.h>
  23. #include "avstring.h"
  24. #include "avfilter.h"
  25. #include "avfiltergraph.h"
  26. typedef struct AVFilterGraph {
  27. unsigned filter_count;
  28. AVFilterContext **filters;
  29. /** fake filter to handle links to internal filters */
  30. AVFilterContext *link_filter;
  31. } GraphContext;
  32. typedef struct {
  33. AVFilterContext *graph;
  34. } GraphLinkContext;
  35. static int link_init(AVFilterContext *ctx, const char *args, void *opaque)
  36. {
  37. GraphLinkContext *linkctx = ctx->priv;
  38. linkctx->graph = opaque;
  39. return !opaque;
  40. }
  41. /* given the link between the dummy filter and an internal filter whose input
  42. * is being exported outside the graph, this returns the externally visible
  43. * link */
  44. static inline AVFilterLink *get_extern_input_link(AVFilterLink *link)
  45. {
  46. GraphLinkContext *lctx = link->src->priv;
  47. return lctx->graph->inputs[link->srcpad];
  48. }
  49. /** query the formats supported by a filter providing input to the graph */
  50. static int *link_in_query_formats(AVFilterLink *link)
  51. {
  52. AVFilterLink *link2 = get_extern_input_link(link);
  53. int *(*query_formats)(AVFilterLink *);
  54. if(!link2)
  55. return avfilter_make_format_list(0);
  56. if(!(query_formats = link2->src->output_pads[link2->srcpad].query_formats))
  57. query_formats = avfilter_default_query_output_formats;
  58. return query_formats(link2);
  59. }
  60. /** request a frame from a filter providing input to the graph */
  61. static void link_in_request_frame(AVFilterLink *link)
  62. {
  63. AVFilterLink *link2 = get_extern_input_link(link);
  64. if(!link2)
  65. return;
  66. avfilter_request_frame(link2);
  67. }
  68. static int link_in_config_props(AVFilterLink *link)
  69. {
  70. AVFilterLink *link2 = get_extern_input_link(link);
  71. int (*config_props)(AVFilterLink *);
  72. int ret;
  73. if(!link2)
  74. return -1;
  75. if(!(config_props = link2->src->output_pads[link2->srcpad].config_props))
  76. config_props = avfilter_default_config_output_link;
  77. ret = config_props(link2);
  78. link->w = link2->w;
  79. link->h = link2->h;
  80. return ret;
  81. }
  82. /* given the link between the dummy filter and an internal filter whose input
  83. * is being exported outside the graph, this returns the externally visible
  84. * link */
  85. static inline AVFilterLink *get_extern_output_link(AVFilterLink *link)
  86. {
  87. GraphLinkContext *lctx = link->dst->priv;
  88. return lctx->graph->outputs[link->dstpad];
  89. }
  90. /** query the formats supported by a filter taking output from the graph */
  91. static int *link_out_query_formats(AVFilterLink *link)
  92. {
  93. AVFilterLink *link2 = get_extern_output_link(link);
  94. if(!link2)
  95. return avfilter_make_format_list(0);
  96. return link2->dst->input_pads[link2->dstpad].query_formats(link2);
  97. }
  98. static int link_out_config_props(AVFilterLink *link)
  99. {
  100. AVFilterLink *link2 = get_extern_output_link(link);
  101. int (*config_props)(AVFilterLink *);
  102. if(!link2)
  103. return 0;
  104. link2->w = link->w;
  105. link2->h = link->h;
  106. if(!(config_props = link2->dst->input_pads[link2->dstpad].config_props))
  107. config_props = avfilter_default_config_input_link;
  108. return config_props(link2);
  109. }
  110. static void link_out_start_frame(AVFilterLink *link, AVFilterPicRef *picref)
  111. {
  112. AVFilterLink *link2 = get_extern_output_link(link);
  113. if(!link2)
  114. avfilter_unref_pic(picref);
  115. else
  116. avfilter_start_frame(link2, picref);
  117. }
  118. static void link_out_end_frame(AVFilterLink *link)
  119. {
  120. AVFilterLink *link2 = get_extern_output_link(link);
  121. if(link2)
  122. avfilter_end_frame(link2);
  123. }
  124. static AVFilterPicRef *link_out_get_video_buffer(AVFilterLink *link, int perms)
  125. {
  126. AVFilterLink *link2 = get_extern_output_link(link);
  127. if(!link2)
  128. return NULL;
  129. else
  130. return avfilter_get_video_buffer(link2, perms);
  131. }
  132. static void link_out_draw_slice(AVFilterLink *link, uint8_t *data[4], int y,
  133. int height)
  134. {
  135. AVFilterLink *link2 = get_extern_output_link(link);
  136. if(link2)
  137. avfilter_draw_slice(link2, data, y, height);
  138. }
  139. /** dummy filter used to help export filters pads outside the graph */
  140. static AVFilter vf_graph_dummy =
  141. {
  142. .name = "graph_dummy",
  143. .author = "Bobby Bingham",
  144. .priv_size = sizeof(GraphLinkContext),
  145. .init = link_init,
  146. //.uninit = uninit,
  147. .inputs = (AVFilterPad[]) {{ .name = NULL, }},
  148. .outputs = (AVFilterPad[]) {{ .name = NULL, }},
  149. };
  150. static AVFilterLink *get_intern_input_link(AVFilterLink *link)
  151. {
  152. GraphContext *graph = link->dst->priv;
  153. return graph->link_filter->outputs[link->dstpad];
  154. }
  155. static void graph_in_start_frame(AVFilterLink *link, AVFilterPicRef *picref)
  156. {
  157. AVFilterLink *link2 = get_intern_input_link(link);
  158. if(link2)
  159. avfilter_start_frame(link2, picref);
  160. }
  161. static void graph_in_end_frame(AVFilterLink *link)
  162. {
  163. AVFilterLink *link2 = get_intern_input_link(link);
  164. if(link2)
  165. avfilter_end_frame(link2);
  166. }
  167. static AVFilterPicRef *graph_in_get_video_buffer(AVFilterLink *link, int perms)
  168. {
  169. AVFilterLink *link2 = get_intern_input_link(link);
  170. if(link2)
  171. return avfilter_get_video_buffer(link2, perms);
  172. return NULL;
  173. }
  174. static void graph_in_draw_slice(AVFilterLink *link, uint8_t *data[4], int y, int height)
  175. {
  176. AVFilterLink *link2 = get_intern_input_link(link);
  177. if(link2)
  178. avfilter_draw_slice(link2, data, y, height);
  179. }
  180. static int *graph_in_query_formats(AVFilterLink *link)
  181. {
  182. AVFilterLink *link2 = get_intern_input_link(link);
  183. if(!link2 || !link2->dst->input_pads[link2->dstpad].query_formats)
  184. return avfilter_make_format_list(0);
  185. return link2->dst->input_pads[link2->dstpad].query_formats(link2);
  186. }
  187. static int graph_in_config_props(AVFilterLink *link)
  188. {
  189. AVFilterLink *link2 = get_intern_input_link(link);
  190. int (*config_props)(AVFilterLink *);
  191. if(!link2)
  192. return -1;
  193. /* copy link properties over to the dummy internal link */
  194. link2->w = link->w;
  195. link2->h = link->h;
  196. if(!(config_props = link2->dst->input_pads[link2->dstpad].config_props))
  197. return 0; /* FIXME? */
  198. //config_props = avfilter_default_config_input_link;
  199. return config_props(link2);
  200. }
  201. static AVFilterLink *get_intern_output_link(AVFilterLink *link)
  202. {
  203. GraphContext *graph = link->src->priv;
  204. return graph->link_filter->inputs[link->srcpad];
  205. }
  206. static int *graph_out_query_formats(AVFilterLink *link)
  207. {
  208. AVFilterLink *link2 = get_intern_output_link(link);
  209. if(!link2)
  210. return avfilter_make_format_list(0);
  211. if(!link2->src->output_pads[link2->srcpad].query_formats)
  212. return avfilter_default_query_output_formats(link2);
  213. return link2->src->output_pads[link2->srcpad].query_formats(link2);
  214. }
  215. static void graph_out_request_frame(AVFilterLink *link)
  216. {
  217. AVFilterLink *link2 = get_intern_output_link(link);
  218. if(link2)
  219. avfilter_request_frame(link2);
  220. }
  221. static int graph_out_config_props(AVFilterLink *link)
  222. {
  223. AVFilterLink *link2 = get_intern_output_link(link);
  224. int (*config_props)(AVFilterLink *);
  225. int ret;
  226. if(!link2)
  227. return 0;
  228. link2->w = link->w;
  229. link2->h = link->h;
  230. link2->format = link->format;
  231. if(!(config_props = link2->src->output_pads[link2->srcpad].config_props))
  232. config_props = avfilter_default_config_output_link;
  233. ret = config_props(link2);
  234. link->w = link2->w;
  235. link->h = link2->h;
  236. link->format = link2->format;
  237. return ret;
  238. }
  239. static int add_graph_input(AVFilterContext *gctx, AVFilterContext *filt, unsigned idx,
  240. char *name)
  241. {
  242. GraphContext *graph = gctx->priv;
  243. AVFilterPad graph_inpad =
  244. {
  245. .name = name,
  246. .type = AV_PAD_VIDEO,
  247. .start_frame = graph_in_start_frame,
  248. .end_frame = graph_in_end_frame,
  249. .get_video_buffer = graph_in_get_video_buffer,
  250. .draw_slice = graph_in_draw_slice,
  251. .query_formats = graph_in_query_formats,
  252. .config_props = graph_in_config_props,
  253. /* XXX */
  254. };
  255. AVFilterPad dummy_outpad =
  256. {
  257. .name = NULL, /* FIXME? */
  258. .type = AV_PAD_VIDEO,
  259. .query_formats = link_in_query_formats,
  260. .request_frame = link_in_request_frame,
  261. .config_props = link_in_config_props,
  262. };
  263. avfilter_insert_inpad (gctx, gctx->input_count, &graph_inpad);
  264. avfilter_insert_outpad(graph->link_filter, graph->link_filter->output_count,
  265. &dummy_outpad);
  266. return avfilter_link(graph->link_filter,
  267. graph->link_filter->output_count-1, filt, idx);
  268. }
  269. static int add_graph_output(AVFilterContext *gctx, AVFilterContext *filt, unsigned idx,
  270. char *name)
  271. {
  272. GraphContext *graph = gctx->priv;
  273. AVFilterPad graph_outpad =
  274. {
  275. .name = name,
  276. .type = AV_PAD_VIDEO,
  277. .request_frame = graph_out_request_frame,
  278. .query_formats = graph_out_query_formats,
  279. .config_props = graph_out_config_props,
  280. };
  281. AVFilterPad dummy_inpad =
  282. {
  283. .name = NULL, /* FIXME? */
  284. .type = AV_PAD_VIDEO,
  285. .start_frame = link_out_start_frame,
  286. .end_frame = link_out_end_frame,
  287. .draw_slice = link_out_draw_slice,
  288. .get_video_buffer = link_out_get_video_buffer,
  289. .query_formats = link_out_query_formats,
  290. .config_props = link_out_config_props,
  291. };
  292. avfilter_insert_outpad(gctx, gctx->output_count, &graph_outpad);
  293. avfilter_insert_inpad (graph->link_filter, graph->link_filter->input_count,
  294. &dummy_inpad);
  295. return avfilter_link(filt, idx, graph->link_filter,
  296. graph->link_filter->input_count-1);
  297. }
  298. static void uninit(AVFilterContext *ctx)
  299. {
  300. GraphContext *graph = ctx->priv;
  301. if(graph->link_filter) {
  302. avfilter_destroy(graph->link_filter);
  303. graph->link_filter = NULL;
  304. }
  305. for(; graph->filter_count > 0; graph->filter_count --)
  306. avfilter_destroy(graph->filters[graph->filter_count - 1]);
  307. av_freep(&graph->filters);
  308. }
  309. void avfilter_graph_add_filter(AVFilterContext *graphctx, AVFilterContext *filter)
  310. {
  311. GraphContext *graph = graphctx->priv;
  312. graph->filters = av_realloc(graph->filters,
  313. sizeof(AVFilterContext*) * ++graph->filter_count);
  314. graph->filters[graph->filter_count - 1] = filter;
  315. }
  316. int avfilter_graph_config_links(AVFilterContext *graphctx)
  317. {
  318. GraphContext *graph = graphctx->priv;
  319. int i, j;
  320. for(i = 0; i < graph->filter_count; i ++) {
  321. for(j = 0; j < graph->filters[i]->input_count; j ++)
  322. if(avfilter_config_link(graph->filters[i]->inputs[j]))
  323. return -1;
  324. }
  325. return 0;
  326. }
  327. static AVFilterContext *create_filter_with_args(const char *filt, void *opaque)
  328. {
  329. AVFilterContext *ret;
  330. char *filter = av_strdup(filt); /* copy - don't mangle the input string */
  331. char *name, *args;
  332. name = filter;
  333. if((args = strchr(filter, '='))) {
  334. /* ensure we at least have a name */
  335. if(args == filter)
  336. goto fail;
  337. *args ++ = 0;
  338. }
  339. av_log(NULL, AV_LOG_INFO, "creating filter \"%s\" with args \"%s\"\n",
  340. name, args ? args : "(none)");
  341. if((ret = avfilter_create_by_name(name, NULL))) {
  342. if(avfilter_init_filter(ret, args, opaque)) {
  343. av_log(NULL, AV_LOG_ERROR, "error initializing filter!\n");
  344. avfilter_destroy(ret);
  345. goto fail;
  346. }
  347. } else av_log(NULL, AV_LOG_ERROR, "error creating filter!\n");
  348. av_free(filter);
  349. return ret;
  350. fail:
  351. av_free(filter);
  352. return NULL;
  353. }
  354. static int graph_load_chain(AVFilterContext *graphctx,
  355. unsigned count, char **filter_list, void **opaque,
  356. AVFilterContext **first, AVFilterContext **last)
  357. {
  358. unsigned i;
  359. AVFilterContext *filters[2] = {NULL,NULL};
  360. for(i = 0; i < count; i ++) {
  361. void *op;
  362. if(opaque) op = opaque[i];
  363. else op = NULL;
  364. if(!(filters[1] = create_filter_with_args(filter_list[i], op)))
  365. goto fail;
  366. if(i == 0) {
  367. if(first) *first = filters[1];
  368. } else {
  369. if(avfilter_link(filters[0], 0, filters[1], 0)) {
  370. av_log(NULL, AV_LOG_ERROR, "error linking filters!\n");
  371. goto fail;
  372. }
  373. }
  374. avfilter_graph_add_filter(graphctx, filters[1]);
  375. if(i == 0 && filters[1]->input_count > 0)
  376. add_graph_input(graphctx, filters[1], 0, "default");
  377. filters[0] = filters[1];
  378. }
  379. if(filters[1]->output_count > 0)
  380. add_graph_output(graphctx, filters[1], 0, "default");
  381. if(last) *last = filters[1];
  382. return 0;
  383. fail:
  384. uninit(graphctx);
  385. if(first) *first = NULL;
  386. if(last) *last = NULL;
  387. return -1;
  388. }
  389. static int graph_load_chain_from_string(AVFilterContext *ctx, const char *str,
  390. AVFilterContext **first,
  391. AVFilterContext **last)
  392. {
  393. int count, ret = 0;
  394. char **strings;
  395. char *filt;
  396. strings = av_malloc(sizeof(char *));
  397. strings[0] = av_strdup(str);
  398. filt = strchr(strings[0], ',');
  399. for(count = 1; filt; count ++) {
  400. if(filt == strings[count-1]) {
  401. ret = -1;
  402. goto done;
  403. }
  404. strings = av_realloc(strings, sizeof(char *) * (count+1));
  405. strings[count] = filt + 1;
  406. *filt = '\0';
  407. filt = strchr(strings[count], ',');
  408. }
  409. ret = graph_load_chain(ctx, count, strings, NULL, first, last);
  410. done:
  411. av_free(strings[0]);
  412. av_free(strings);
  413. return ret;
  414. }
  415. static int init(AVFilterContext *ctx, const char *args, void *opaque)
  416. {
  417. GraphContext *gctx = ctx->priv;
  418. if(!args)
  419. return 0;
  420. if(!(gctx->link_filter = avfilter_create(&vf_graph_dummy, NULL)))
  421. return -1;
  422. if(avfilter_init_filter(gctx->link_filter, NULL, ctx))
  423. goto fail;
  424. return graph_load_chain_from_string(ctx, args, NULL, NULL);
  425. fail:
  426. avfilter_destroy(gctx->link_filter);
  427. return -1;
  428. }
  429. AVFilter vf_graph =
  430. {
  431. .name = "graph",
  432. .author = "Bobby Bingham",
  433. .priv_size = sizeof(GraphContext),
  434. .init = init,
  435. .uninit = uninit,
  436. .inputs = (AVFilterPad[]) {{ .name = NULL, }},
  437. .outputs = (AVFilterPad[]) {{ .name = NULL, }},
  438. };