Commited in SoC by Bobby Bingham on 2007-08-07 22:33:46 Originally committed as revision 12696 to svn://svn.ffmpeg.org/ffmpeg/trunk
Showing the changes to libavfilter/avfiltergraph.c (1 of 1 file). Show all
Unified · Split
| @@ -29,12 +29,344 @@ · 28 unmodified lines | ||
| 29 | 29 | typedef struct AVFilterGraph { |
| 30 | 30 | unsigned filter_count; |
| 31 | 31 | AVFilterContext **filters; |
| 32 | + | |
| 33 | + /** fake filter to handle links to internal filters */ | |
| 34 | + AVFilterContext *link_filter; | |
| 32 | 35 | } GraphContext; |
| 33 | 36 | |
| 37 | +typedef struct { | |
| 38 | + AVFilterContext *graph; | |
| 39 | +} GraphLinkContext; | |
| 40 | + | |
| 41 | +static int link_init(AVFilterContext *ctx, const char *args, void *opaque) | |
| 42 | +{ | |
| 43 | + GraphLinkContext *linkctx = ctx->priv; | |
| 44 | + linkctx->graph = opaque; | |
| 45 | + return !opaque; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* given the link between the dummy filter and an internal filter whose input | |
| 49 | + * is being exported outside the graph, this returns the externally visible | |
| 50 | + * link */ | |
| 51 | +static inline AVFilterLink *get_extern_input_link(AVFilterLink *link) | |
| 52 | +{ | |
| 53 | + GraphLinkContext *lctx = link->src->priv; | |
| 54 | + return lctx->graph->inputs[link->srcpad]; | |
| 55 | +} | |
| 56 | + | |
| 57 | +/** query the formats supported by a filter providing input to the graph */ | |
| 58 | +static int *link_in_query_formats(AVFilterLink *link) | |
| 59 | +{ | |
| 60 | + AVFilterLink *link2 = get_extern_input_link(link); | |
| 61 | + int *(*query_formats)(AVFilterLink *); | |
| 62 | + | |
| 63 | + if(!link2) | |
| 64 | + return avfilter_make_format_list(0); | |
| 65 | + | |
| 66 | + if(!(query_formats = link2->src->output_pads[link2->srcpad].query_formats)) | |
| 67 | + query_formats = avfilter_default_query_output_formats; | |
| 68 | + | |
| 69 | + return query_formats(link2); | |
| 70 | +} | |
| 71 | + | |
| 72 | +/** request a frame from a filter providing input to the graph */ | |
| 73 | +static void link_in_request_frame(AVFilterLink *link) | |
| 74 | +{ | |
| 75 | + AVFilterLink *link2 = get_extern_input_link(link); | |
| 76 | + | |
| 77 | + if(!link2) | |
| 78 | + return; | |
| 79 | + avfilter_request_frame(link2); | |
| 80 | +} | |
| 81 | + | |
| 82 | +static int link_in_config_props(AVFilterLink *link) | |
| 83 | +{ | |
| 84 | + AVFilterLink *link2 = get_extern_input_link(link); | |
| 85 | + int (*config_props)(AVFilterLink *); | |
| 86 | + int ret; | |
| 87 | + | |
| 88 | + if(!link2) | |
| 89 | + return -1; | |
| 90 | + if(!(config_props = link2->src->output_pads[link2->srcpad].config_props)) | |
| 91 | + config_props = avfilter_default_config_output_link; | |
| 92 | + ret = config_props(link2); | |
| 93 | + | |
| 94 | + link->w = link2->w; | |
| 95 | + link->h = link2->h; | |
| 96 | + | |
| 97 | + return ret; | |
| 98 | +} | |
| 99 | + | |
| 100 | +/* given the link between the dummy filter and an internal filter whose input | |
| 101 | + * is being exported outside the graph, this returns the externally visible | |
| 102 | + * link */ | |
| 103 | +static inline AVFilterLink *get_extern_output_link(AVFilterLink *link) | |
| 104 | +{ | |
| 105 | + GraphLinkContext *lctx = link->dst->priv; | |
| 106 | + return lctx->graph->outputs[link->dstpad]; | |
| 107 | +} | |
| 108 | + | |
| 109 | +/** query the formats supported by a filter taking output from the graph */ | |
| 110 | +static int *link_out_query_formats(AVFilterLink *link) | |
| 111 | +{ | |
| 112 | + AVFilterLink *link2 = get_extern_output_link(link); | |
| 113 | + | |
| 114 | + if(!link2) | |
| 115 | + return avfilter_make_format_list(0); | |
| 116 | + | |
| 117 | + return link2->dst->input_pads[link2->dstpad].query_formats(link2); | |
| 118 | +} | |
| 119 | + | |
| 120 | +static int link_out_config_props(AVFilterLink *link) | |
| 121 | +{ | |
| 122 | + AVFilterLink *link2 = get_extern_output_link(link); | |
| 123 | + int (*config_props)(AVFilterLink *); | |
| 124 | + | |
| 125 | + if(!link2) | |
| 126 | + return 0; | |
| 127 | + | |
| 128 | + link2->w = link->w; | |
| 129 | + link2->h = link->h; | |
| 130 | + | |
| 131 | + if(!(config_props = link2->dst->input_pads[link2->dstpad].config_props)) | |
| 132 | + config_props = avfilter_default_config_input_link; | |
| 133 | + return config_props(link2); | |
| 134 | +} | |
| 135 | + | |
| 136 | +static void link_out_start_frame(AVFilterLink *link, AVFilterPicRef *picref) | |
| 137 | +{ | |
| 138 | + AVFilterLink *link2 = get_extern_output_link(link); | |
| 139 | + | |
| 140 | + if(!link2) | |
| 141 | + avfilter_unref_pic(picref); | |
| 142 | + else | |
| 143 | + avfilter_start_frame(link2, picref); | |
| 144 | +} | |
| 145 | + | |
| 146 | +static void link_out_end_frame(AVFilterLink *link) | |
| 147 | +{ | |
| 148 | + AVFilterLink *link2 = get_extern_output_link(link); | |
| 149 | + | |
| 150 | + if(link2) | |
| 151 | + avfilter_end_frame(link2); | |
| 152 | +} | |
| 153 | + | |
| 154 | +static AVFilterPicRef *link_out_get_video_buffer(AVFilterLink *link, int perms) | |
| 155 | +{ | |
| 156 | + AVFilterLink *link2 = get_extern_output_link(link); | |
| 157 | + | |
| 158 | + if(!link2) | |
| 159 | + return NULL; | |
| 160 | + else | |
| 161 | + return avfilter_get_video_buffer(link2, perms); | |
| 162 | +} | |
| 163 | + | |
| 164 | +static void link_out_draw_slice(AVFilterLink *link, uint8_t *data[4], int y, | |
| 165 | + int height) | |
| 166 | +{ | |
| 167 | + AVFilterLink *link2 = get_extern_output_link(link); | |
| 168 | + | |
| 169 | + if(link2) | |
| 170 | + avfilter_draw_slice(link2, data, y, height); | |
| 171 | +} | |
| 172 | + | |
| 173 | +/** dummy filter used to help export filters pads outside the graph */ | |
| 174 | +static AVFilter vf_graph_dummy = | |
| 175 | +{ | |
| 176 | + .name = "graph_dummy", | |
| 177 | + .author = "Bobby Bingham", | |
| 178 | + | |
| 179 | + .priv_size = sizeof(GraphLinkContext), | |
| 180 | + | |
| 181 | + .init = link_init, | |
| 182 | + //.uninit = uninit, | |
| 183 | + | |
| 184 | + .inputs = (AVFilterPad[]) {{ .name = NULL, }}, | |
| 185 | + .outputs = (AVFilterPad[]) {{ .name = NULL, }}, | |
| 186 | +}; | |
| 187 | + | |
| 188 | +static AVFilterLink *get_intern_input_link(AVFilterLink *link) | |
| 189 | +{ | |
| 190 | + GraphContext *graph = link->dst->priv; | |
| 191 | + return graph->link_filter->outputs[link->dstpad]; | |
| 192 | +} | |
| 193 | + | |
| 194 | +static void graph_in_start_frame(AVFilterLink *link, AVFilterPicRef *picref) | |
| 195 | +{ | |
| 196 | + AVFilterLink *link2 = get_intern_input_link(link); | |
| 197 | + if(link2) | |
| 198 | + avfilter_start_frame(link2, picref); | |
| 199 | +} | |
| 200 | + | |
| 201 | +static void graph_in_end_frame(AVFilterLink *link) | |
| 202 | +{ | |
| 203 | + AVFilterLink *link2 = get_intern_input_link(link); | |
| 204 | + if(link2) | |
| 205 | + avfilter_end_frame(link2); | |
| 206 | +} | |
| 207 | + | |
| 208 | +static AVFilterPicRef *graph_in_get_video_buffer(AVFilterLink *link, int perms) | |
| 209 | +{ | |
| 210 | + AVFilterLink *link2 = get_intern_input_link(link); | |
| 211 | + if(link2) | |
| 212 | + return avfilter_get_video_buffer(link2, perms); | |
| 213 | + return NULL; | |
| 214 | +} | |
| 215 | + | |
| 216 | +static void graph_in_draw_slice(AVFilterLink *link, uint8_t *data[4], int y, int height) | |
| 217 | +{ | |
| 218 | + AVFilterLink *link2 = get_intern_input_link(link); | |
| 219 | + if(link2) | |
| 220 | + avfilter_draw_slice(link2, data, y, height); | |
| 221 | +} | |
| 222 | + | |
| 223 | +static int *graph_in_query_formats(AVFilterLink *link) | |
| 224 | +{ | |
| 225 | + AVFilterLink *link2 = get_intern_input_link(link); | |
| 226 | + | |
| 227 | + if(!link2 || !link2->dst->input_pads[link2->dstpad].query_formats) | |
| 228 | + return avfilter_make_format_list(0); | |
| 229 | + return link2->dst->input_pads[link2->dstpad].query_formats(link2); | |
| 230 | +} | |
| 231 | + | |
| 232 | +static int graph_in_config_props(AVFilterLink *link) | |
| 233 | +{ | |
| 234 | + AVFilterLink *link2 = get_intern_input_link(link); | |
| 235 | + int (*config_props)(AVFilterLink *); | |
| 236 | + | |
| 237 | + if(!link2) | |
| 238 | + return -1; | |
| 239 | + | |
| 240 | + /* copy link properties over to the dummy internal link */ | |
| 241 | + link2->w = link->w; | |
| 242 | + link2->h = link->h; | |
| 243 | + | |
| 244 | + if(!(config_props = link2->dst->input_pads[link2->dstpad].config_props)) | |
| 245 | + return 0; /* FIXME? */ | |
| 246 | + //config_props = avfilter_default_config_input_link; | |
| 247 | + return config_props(link2); | |
| 248 | +} | |
| 249 | + | |
| 250 | +static AVFilterLink *get_intern_output_link(AVFilterLink *link) | |
| 251 | +{ | |
| 252 | + GraphContext *graph = link->src->priv; | |
| 253 | + return graph->link_filter->inputs[link->srcpad]; | |
| 254 | +} | |
| 255 | + | |
| 256 | +static int *graph_out_query_formats(AVFilterLink *link) | |
| 257 | +{ | |
| 258 | + AVFilterLink *link2 = get_intern_output_link(link); | |
| 259 | + | |
| 260 | + if(!link2 || !link2->src->output_pads[link2->srcpad].query_formats) | |
| 261 | + return avfilter_make_format_list(0); | |
| 262 | + return link2->src->output_pads[link2->srcpad].query_formats(link2); | |
| 263 | +} | |
| 264 | + | |
| 265 | +static void graph_out_request_frame(AVFilterLink *link) | |
| 266 | +{ | |
| 267 | + AVFilterLink *link2 = get_intern_output_link(link); | |
| 268 | + | |
| 269 | + if(link2) | |
| 270 | + avfilter_request_frame(link2); | |
| 271 | +} | |
| 272 | + | |
| 273 | +static int graph_out_config_props(AVFilterLink *link) | |
| 274 | +{ | |
| 275 | + AVFilterLink *link2 = get_intern_output_link(link); | |
| 276 | + int (*config_props)(AVFilterLink *); | |
| 277 | + int ret; | |
| 278 | + | |
| 279 | + if(!link2) | |
| 280 | + return 0; | |
| 281 | + | |
| 282 | + link2->w = link->w; | |
| 283 | + link2->h = link->h; | |
| 284 | + link2->format = link->format; | |
| 285 | + | |
| 286 | + if(!(config_props = link2->src->output_pads[link2->srcpad].config_props)) | |
| 287 | + config_props = avfilter_default_config_output_link; | |
| 288 | + ret = config_props(link2); | |
| 289 | + | |
| 290 | + link->w = link2->w; | |
| 291 | + link->h = link2->h; | |
| 292 | + link->format = link2->format; | |
| 293 | + | |
| 294 | + return ret; | |
| 295 | +} | |
| 296 | + | |
| 297 | +static int add_graph_input(AVFilterContext *gctx, AVFilterContext *filt, unsigned idx, | |
| 298 | + char *name) | |
| 299 | +{ | |
| 300 | + GraphContext *graph = gctx->priv; | |
| 301 | + | |
| 302 | + AVFilterPad graph_inpad = | |
| 303 | + { | |
| 304 | + .name = name, | |
| 305 | + .type = AV_PAD_VIDEO, | |
| 306 | + .start_frame = graph_in_start_frame, | |
| 307 | + .end_frame = graph_in_end_frame, | |
| 308 | + .get_video_buffer = graph_in_get_video_buffer, | |
| 309 | + .draw_slice = graph_in_draw_slice, | |
| 310 | + .query_formats = graph_in_query_formats, | |
| 311 | + .config_props = graph_in_config_props, | |
| 312 | + /* XXX */ | |
| 313 | + }; | |
| 314 | + AVFilterPad dummy_outpad = | |
| 315 | + { | |
| 316 | + .name = NULL, /* FIXME? */ | |
| 317 | + .type = AV_PAD_VIDEO, | |
| 318 | + .query_formats = link_in_query_formats, | |
| 319 | + .request_frame = link_in_request_frame, | |
| 320 | + .config_props = link_in_config_props, | |
| 321 | + }; | |
| 322 | + | |
| 323 | + avfilter_insert_inpad (gctx, gctx->input_count, &graph_inpad); | |
| 324 | + avfilter_insert_outpad(graph->link_filter, graph->link_filter->output_count, | |
| 325 | + &dummy_outpad); | |
| 326 | + return avfilter_link(graph->link_filter, | |
| 327 | + graph->link_filter->output_count-1, filt, idx); | |
| 328 | +} | |
| 329 | + | |
| 330 | +static int add_graph_output(AVFilterContext *gctx, AVFilterContext *filt, unsigned idx, | |
| 331 | + char *name) | |
| 332 | +{ | |
| 333 | + GraphContext *graph = gctx->priv; | |
| 334 | + | |
| 335 | + AVFilterPad graph_outpad = | |
| 336 | + { | |
| 337 | + .name = name, | |
| 338 | + .type = AV_PAD_VIDEO, | |
| 339 | + .request_frame = graph_out_request_frame, | |
| 340 | + .query_formats = graph_out_query_formats, | |
| 341 | + .config_props = graph_out_config_props, | |
| 342 | + }; | |
| 343 | + AVFilterPad dummy_inpad = | |
| 344 | + { | |
| 345 | + .name = NULL, /* FIXME? */ | |
| 346 | + .type = AV_PAD_VIDEO, | |
| 347 | + .start_frame = link_out_start_frame, | |
| 348 | + .end_frame = link_out_end_frame, | |
| 349 | + .draw_slice = link_out_draw_slice, | |
| 350 | + .get_video_buffer = link_out_get_video_buffer, | |
| 351 | + .query_formats = link_out_query_formats, | |
| 352 | + .config_props = link_out_config_props, | |
| 353 | + }; | |
| 354 | + | |
| 355 | + avfilter_insert_outpad(gctx, gctx->output_count, &graph_outpad); | |
| 356 | + avfilter_insert_inpad (graph->link_filter, graph->link_filter->input_count, | |
| 357 | + &dummy_inpad); | |
| 358 | + return avfilter_link(filt, idx, graph->link_filter, | |
| 359 | + graph->link_filter->input_count-1); | |
| 360 | +} | |
| 361 | + | |
| 34 | 362 | static void uninit(AVFilterContext *ctx) |
| 35 | 363 | { |
| 36 | 364 | GraphContext *graph = ctx->priv; |
| 37 | 365 | |
| 366 | + if(graph->link_filter) { | |
| 367 | + avfilter_destroy(graph->link_filter); | |
| 368 | + graph->link_filter = NULL; | |
| 369 | + } | |
| 38 | 370 | for(; graph->filter_count > 0; graph->filter_count --) |
| 39 | 371 | avfilter_destroy(graph->filters[graph->filter_count - 1]); |
| 40 | 372 | av_freep(&graph->filters); |
| @@ -122,9 +454,14 @@ static int graph_load_chain(AVFilterContext *graphctx, · 81 unmodified lines | ||
| 122 | 454 | } |
| 123 | 455 | } |
| 124 | 456 | avfilter_graph_add_filter(graphctx, filters[1]); |
| 457 | + if(i == 0 && filters[1]->input_count > 0) | |
| 458 | + add_graph_input(graphctx, filters[1], 0, "default"); | |
| 125 | 459 | filters[0] = filters[1]; |
| 126 | 460 | } |
| 127 | 461 | |
| 462 | + if(filters[1]->output_count > 0) | |
| 463 | + add_graph_output(graphctx, filters[1], 0, "default"); | |
| 464 | + | |
| 128 | 465 | if(last) *last = filters[1]; |
| 129 | 466 | return 0; |
| 130 | 467 | |
| @@ -170,14 +507,21 @@ done: · 39 unmodified lines | ||
| 170 | 507 | |
| 171 | 508 | static int init(AVFilterContext *ctx, const char *args, void *opaque) |
| 172 | 509 | { |
| 173 | − AVFilterContext **filters = opaque; | |
| 510 | + GraphContext *gctx = ctx->priv; | |
| 174 | 511 | |
| 175 | 512 | if(!args) |
| 176 | 513 | return 0; |
| 177 | − if(!opaque) | |
| 514 | + | |
| 515 | + if(!(gctx->link_filter = avfilter_create(&vf_graph_dummy, NULL))) | |
| 178 | 516 | return -1; |
| 517 | + if(avfilter_init_filter(gctx->link_filter, NULL, ctx)) | |
| 518 | + goto fail; | |
| 179 | 519 | |
| 180 | − return graph_load_chain_from_string(ctx, args, filters, filters + 1); | |
| 520 | + return graph_load_chain_from_string(ctx, args, NULL, NULL); | |
| 521 | + | |
| 522 | +fail: | |
| 523 | + avfilter_destroy(gctx->link_filter); | |
| 524 | + return -1; | |
| 181 | 525 | } |
| 182 | 526 | |
| 183 | 527 | AVFilter vf_graph = |