X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Favfilter.c;h=99ed8fab3b818e64b3dabef8b9e11fe9e352270f;hb=801ab9808863415a80a576b26c45cb06187cc239;hp=08007dc221d008ba0173fdcdbe5c5302373127d2;hpb=44b73497d31ffad6e0c89927006edeb58654fac3;p=ffmpeg diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 08007dc221d..99ed8fab3b8 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -1,5 +1,5 @@ /* - * Filter layer + * filter layer * copyright (c) 2007 Bobby Bingham * * This file is part of FFmpeg. @@ -19,38 +19,65 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include -#include +/* #define DEBUG */ +#include "libavcodec/audioconvert.c" +#include "libavutil/pixdesc.h" +#include "libavcore/imgutils.h" #include "avfilter.h" -#include "allfilters.h" +#include "internal.h" -/** list of registered filters */ -struct FilterList +unsigned avfilter_version(void) { + return LIBAVFILTER_VERSION_INT; +} + +const char *avfilter_configuration(void) { - AVFilter *filter; - struct FilterList *next; -} *filters = NULL; + return FFMPEG_CONFIGURATION; +} + +const char *avfilter_license(void) +{ +#define LICENSE_PREFIX "libavfilter license: " + return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; +} /** helper macros to get the in/out pad on the dst/src filter */ #define link_dpad(link) link->dst-> input_pads[link->dstpad] #define link_spad(link) link->src->output_pads[link->srcpad] -AVFilterPicRef *avfilter_ref_pic(AVFilterPicRef *ref, int pmask) +AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask) { - AVFilterPicRef *ret = av_malloc(sizeof(AVFilterPicRef)); + AVFilterBufferRef *ret = av_malloc(sizeof(AVFilterBufferRef)); + if (!ret) + return NULL; *ret = *ref; + if (ref->type == AVMEDIA_TYPE_VIDEO) { + ret->video = av_malloc(sizeof(AVFilterBufferRefVideoProps)); + if (!ret->video) { + av_free(ret); + return NULL; + } + *ret->video = *ref->video; + } else if (ref->type == AVMEDIA_TYPE_AUDIO) { + ret->audio = av_malloc(sizeof(AVFilterBufferRefAudioProps)); + if (!ret->audio) { + av_free(ret); + return NULL; + } + *ret->audio = *ref->audio; + } ret->perms &= pmask; - ret->pic->refcount ++; + ret->buf->refcount ++; return ret; } -void avfilter_unref_pic(AVFilterPicRef *ref) +void avfilter_unref_buffer(AVFilterBufferRef *ref) { - if(-- ref->pic->refcount == 0) - ref->pic->free(ref->pic); + if (!(--ref->buf->refcount)) + ref->buf->free(ref->buf); + av_free(ref->video); + av_free(ref->audio); av_free(ref); } @@ -69,10 +96,10 @@ void avfilter_insert_pad(unsigned idx, unsigned *count, size_t padidx_off, memcpy(*pads+idx, newpad, sizeof(AVFilterPad)); (*links)[idx] = NULL; - (*count) ++; - for(i = idx+1; i < *count; i ++) - if(*links[i]) - (*(unsigned *)((uint8_t *)(*links[i]) + padidx_off)) ++; + (*count)++; + for (i = idx+1; i < *count; i++) + if (*links[i]) + (*(unsigned *)((uint8_t *) *links[i] + padidx_off))++; } int avfilter_link(AVFilterContext *src, unsigned srcpad, @@ -80,221 +107,351 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, { AVFilterLink *link; - if(src->output_count <= srcpad || dst->input_count <= dstpad || - src->outputs[srcpad] || dst->inputs[dstpad]) + if (src->output_count <= srcpad || dst->input_count <= dstpad || + src->outputs[srcpad] || dst->inputs[dstpad]) return -1; src->outputs[srcpad] = - dst->inputs[dstpad] = link = av_mallocz(sizeof(AVFilterLink)); + dst-> inputs[dstpad] = link = av_mallocz(sizeof(AVFilterLink)); link->src = src; link->dst = dst; link->srcpad = srcpad; link->dstpad = dstpad; + link->type = src->output_pads[srcpad].type; + assert(PIX_FMT_NONE == -1 && SAMPLE_FMT_NONE == -1); link->format = -1; return 0; } -int avfilter_config_link(AVFilterLink *link) +int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, + unsigned in, unsigned out) +{ + av_log(link->dst, AV_LOG_INFO, "auto-inserting filter '%s' " + "between the filter '%s' and the filter '%s'\n", + filt->name, link->src->name, link->dst->name); + + link->dst->inputs[link->dstpad] = NULL; + if (avfilter_link(filt, out, link->dst, link->dstpad)) { + /* failed to link output filter to new filter */ + link->dst->inputs[link->dstpad] = link; + return -1; + } + + /* re-hookup the link to the new destination filter we inserted */ + link->dst = filt; + link->dstpad = in; + filt->inputs[in] = link; + + /* if any information on supported media formats already exists on the + * link, we need to preserve that */ + if (link->out_formats) + avfilter_formats_changeref(&link->out_formats, + &filt->outputs[out]->out_formats); + + return 0; +} + +int avfilter_config_links(AVFilterContext *filter) { - int *fmts[2], i, j; int (*config_link)(AVFilterLink *); - int *(*query_formats)(AVFilterLink *link); - - if(!link) - return 0; - - /* find a format both filters support - TODO: auto-insert conversion filter */ - link->format = -1; - if(!(query_formats = link_spad(link).query_formats)) - query_formats = avfilter_default_query_output_formats; - fmts[0] = query_formats(link); - fmts[1] = link_dpad(link).query_formats(link); - for(i = 0; fmts[0][i] != -1; i ++) - for(j = 0; fmts[1][j] != -1; j ++) - if(fmts[0][i] == fmts[1][j]) { - link->format = fmts[0][i]; - goto format_done; - } + unsigned i; -format_done: - av_free(fmts[0]); - av_free(fmts[1]); - if(link->format == -1) - return -1; + for (i = 0; i < filter->input_count; i ++) { + AVFilterLink *link = filter->inputs[i]; - if(!(config_link = link_spad(link).config_props)) - config_link = avfilter_default_config_output_link; - if(config_link(link)) - return -1; + if (!link) continue; - if(!(config_link = link_dpad(link).config_props)) - config_link = avfilter_default_config_input_link; - if(config_link(link)) - return -1; + switch (link->init_state) { + case AVLINK_INIT: + continue; + case AVLINK_STARTINIT: + av_log(filter, AV_LOG_INFO, "circular filter chain detected\n"); + return 0; + case AVLINK_UNINIT: + link->init_state = AVLINK_STARTINIT; + + if (avfilter_config_links(link->src)) + return -1; + + if (!(config_link = link_spad(link).config_props)) + config_link = avfilter_default_config_output_link; + if (config_link(link)) + return -1; + + if ((config_link = link_dpad(link).config_props)) + if (config_link(link)) + return -1; + + link->init_state = AVLINK_INIT; + } + } return 0; } -AVFilterPicRef *avfilter_get_video_buffer(AVFilterLink *link, int perms) +void ff_dprintf_picref(void *ctx, AVFilterBufferRef *picref, int end) +{ + dprintf(ctx, + "picref[%p data:%p linesize[%d, %d, %d, %d] pts:%"PRId64" pos:%"PRId64, + picref, + picref->data[0], + picref->linesize[0], picref->linesize[1], picref->linesize[2], picref->linesize[3], + picref->pts, picref->pos); + + if (picref->video) { + dprintf(ctx, " a:%d/%d s:%dx%d i:%c", + picref->video->pixel_aspect.num, picref->video->pixel_aspect.den, + picref->video->w, picref->video->h, + !picref->video->interlaced ? 'P' : /* Progressive */ + picref->video->top_field_first ? 'T' : 'B'); /* Top / Bottom */ + } + dprintf(ctx, "]%s", end ? "\n" : ""); +} + +void ff_dprintf_link(void *ctx, AVFilterLink *link, int end) { - AVFilterPicRef *ret = NULL; + dprintf(ctx, + "link[%p s:%dx%d fmt:%-16s %-16s->%-16s]%s", + link, link->w, link->h, + av_pix_fmt_descriptors[link->format].name, + link->src ? link->src->filter->name : "", + link->dst ? link->dst->filter->name : "", + end ? "\n" : ""); +} + +AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link, int perms, int w, int h) +{ + AVFilterBufferRef *ret = NULL; + + FF_DPRINTF_START(NULL, get_video_buffer); ff_dprintf_link(NULL, link, 0); dprintf(NULL, " perms:%d w:%d h:%d\n", perms, w, h); + + if (link_dpad(link).get_video_buffer) + ret = link_dpad(link).get_video_buffer(link, perms, w, h); + + if (!ret) + ret = avfilter_default_get_video_buffer(link, perms, w, h); - if(link_dpad(link).get_video_buffer) - ret = link_dpad(link).get_video_buffer(link, perms); + if (ret) + ret->type = AVMEDIA_TYPE_VIDEO; - if(!ret) - ret = avfilter_default_get_video_buffer(link, perms); + FF_DPRINTF_START(NULL, get_video_buffer); ff_dprintf_link(NULL, link, 0); dprintf(NULL, " returning "); ff_dprintf_picref(NULL, ret, 1); + + return ret; +} + +AVFilterBufferRef *avfilter_get_audio_buffer(AVFilterLink *link, int perms, + enum SampleFormat sample_fmt, int size, + int64_t channel_layout, int planar) +{ + AVFilterBufferRef *ret = NULL; + + if (link_dpad(link).get_audio_buffer) + ret = link_dpad(link).get_audio_buffer(link, perms, sample_fmt, size, channel_layout, planar); + + if (!ret) + ret = avfilter_default_get_audio_buffer(link, perms, sample_fmt, size, channel_layout, planar); + + if (ret) + ret->type = AVMEDIA_TYPE_AUDIO; return ret; } int avfilter_request_frame(AVFilterLink *link) { - if(link_spad(link).request_frame) + FF_DPRINTF_START(NULL, request_frame); ff_dprintf_link(NULL, link, 1); + + if (link_spad(link).request_frame) return link_spad(link).request_frame(link); - else if(link->src->inputs[0]) + else if (link->src->inputs[0]) return avfilter_request_frame(link->src->inputs[0]); else return -1; } +int avfilter_poll_frame(AVFilterLink *link) +{ + int i, min = INT_MAX; + + if (link_spad(link).poll_frame) + return link_spad(link).poll_frame(link); + + for (i = 0; i < link->src->input_count; i++) { + int val; + if (!link->src->inputs[i]) + return -1; + val = avfilter_poll_frame(link->src->inputs[i]); + min = FFMIN(min, val); + } + + return min; +} + /* XXX: should we do the duplicating of the picture ref here, instead of * forcing the source filter to do it? */ -void avfilter_start_frame(AVFilterLink *link, AVFilterPicRef *picref) +void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) { - void (*start_frame)(AVFilterLink *, AVFilterPicRef *); + void (*start_frame)(AVFilterLink *, AVFilterBufferRef *); + AVFilterPad *dst = &link_dpad(link); + + FF_DPRINTF_START(NULL, start_frame); ff_dprintf_link(NULL, link, 0); dprintf(NULL, " "); ff_dprintf_picref(NULL, picref, 1); - if(!(start_frame = link_dpad(link).start_frame)) + if (!(start_frame = dst->start_frame)) start_frame = avfilter_default_start_frame; /* prepare to copy the picture if it has insufficient permissions */ - if((link_dpad(link).min_perms & picref->perms) != link_dpad(link).min_perms || - link_dpad(link).rej_perms & picref->perms) { - av_log(link->dst, AV_LOG_INFO, + if ((dst->min_perms & picref->perms) != dst->min_perms || + dst->rej_perms & picref->perms) { + av_log(link->dst, AV_LOG_DEBUG, "frame copy needed (have perms %x, need %x, reject %x)\n", picref->perms, link_dpad(link).min_perms, link_dpad(link).rej_perms); - link->cur_pic = avfilter_default_get_video_buffer(link, link_dpad(link).min_perms); - link->srcpic = picref; + link->cur_buf = avfilter_default_get_video_buffer(link, dst->min_perms, link->w, link->h); + link->src_buf = picref; + avfilter_copy_buffer_ref_props(link->cur_buf, link->src_buf); } else - link->cur_pic = picref; + link->cur_buf = picref; - start_frame(link, link->cur_pic); + start_frame(link, link->cur_buf); } void avfilter_end_frame(AVFilterLink *link) { void (*end_frame)(AVFilterLink *); - /* unreference the source picture if we're feeding the destination filter - * a copied version dues to permission issues */ - if(link->srcpic) { - avfilter_unref_pic(link->srcpic); - link->srcpic = NULL; - } - - if(!(end_frame = link_dpad(link).end_frame)) + if (!(end_frame = link_dpad(link).end_frame)) end_frame = avfilter_default_end_frame; end_frame(link); + + /* unreference the source picture if we're feeding the destination filter + * a copied version dues to permission issues */ + if (link->src_buf) { + avfilter_unref_buffer(link->src_buf); + link->src_buf = NULL; + } } -void avfilter_draw_slice(AVFilterLink *link, int y, int h) +void avfilter_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { uint8_t *src[4], *dst[4]; - int i, j, hsub, vsub; + int i, j, vsub; + void (*draw_slice)(AVFilterLink *, int, int, int); + + FF_DPRINTF_START(NULL, draw_slice); ff_dprintf_link(NULL, link, 0); dprintf(NULL, " y:%d h:%d dir:%d\n", y, h, slice_dir); /* copy the slice if needed for permission reasons */ - if(link->srcpic) { - avcodec_get_chroma_sub_sample(link->format, &hsub, &vsub); - - src[0] = link->srcpic-> data[0] + y * link->srcpic-> linesize[0]; - dst[0] = link->cur_pic->data[0] + y * link->cur_pic->linesize[0]; - for(i = 1; i < 4; i ++) { - if(link->srcpic->data[i]) { - src[i] = link->srcpic-> data[i] + (y >> vsub) * link->srcpic-> linesize[i]; - dst[i] = link->cur_pic->data[i] + (y >> vsub) * link->cur_pic->linesize[i]; + if (link->src_buf) { + vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h; + + for (i = 0; i < 4; i++) { + if (link->src_buf->data[i]) { + src[i] = link->src_buf-> data[i] + + (y >> (i==0 ? 0 : vsub)) * link->src_buf-> linesize[i]; + dst[i] = link->cur_buf->data[i] + + (y >> (i==0 ? 0 : vsub)) * link->cur_buf->linesize[i]; } else src[i] = dst[i] = NULL; } - for(j = 0; j < h; j ++) { - memcpy(dst[0], src[0], link->cur_pic->linesize[0]); - src[0] += link->srcpic ->linesize[0]; - dst[0] += link->cur_pic->linesize[0]; - } - for(i = 1; i < 4; i ++) { - if(!src[i]) continue; - for(j = 0; j < h >> vsub; j ++) { - memcpy(dst[i], src[i], link->cur_pic->linesize[i]); - src[i] += link->srcpic ->linesize[i]; - dst[i] += link->cur_pic->linesize[i]; + for (i = 0; i < 4; i++) { + int planew = + av_get_image_linesize(link->format, link->cur_buf->video->w, i); + + if (!src[i]) continue; + + for (j = 0; j < h >> (i==0 ? 0 : vsub); j++) { + memcpy(dst[i], src[i], planew); + src[i] += link->src_buf->linesize[i]; + dst[i] += link->cur_buf->linesize[i]; } } } - if(!link_dpad(link).draw_slice) - return; + if (!(draw_slice = link_dpad(link).draw_slice)) + draw_slice = avfilter_default_draw_slice; + draw_slice(link, y, h, slice_dir); +} + +void avfilter_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) +{ + void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *); + AVFilterPad *dst = &link_dpad(link); + + if (!(filter_samples = dst->filter_samples)) + filter_samples = avfilter_default_filter_samples; + + /* prepare to copy the samples if the buffer has insufficient permissions */ + if ((dst->min_perms & samplesref->perms) != dst->min_perms || + dst->rej_perms & samplesref->perms) { + + av_log(link->dst, AV_LOG_DEBUG, + "Copying audio data in avfilter (have perms %x, need %x, reject %x)\n", + samplesref->perms, link_dpad(link).min_perms, link_dpad(link).rej_perms); + + link->cur_buf = avfilter_default_get_audio_buffer(link, dst->min_perms, + samplesref->format, + samplesref->audio->size, + samplesref->audio->channel_layout, + samplesref->audio->planar); + link->cur_buf->pts = samplesref->pts; + link->cur_buf->audio->sample_rate = samplesref->audio->sample_rate; - link_dpad(link).draw_slice(link, y, h); + /* Copy actual data into new samples buffer */ + memcpy(link->cur_buf->data[0], samplesref->data[0], samplesref->audio->size); + + avfilter_unref_buffer(samplesref); + } else + link->cur_buf = samplesref; + + filter_samples(link, link->cur_buf); } -AVFilter *avfilter_get_by_name(char *name) +#define MAX_REGISTERED_AVFILTERS_NB 64 + +static AVFilter *registered_avfilters[MAX_REGISTERED_AVFILTERS_NB + 1]; + +static int next_registered_avfilter_idx = 0; + +AVFilter *avfilter_get_by_name(const char *name) { - struct FilterList *filt; + int i; - for(filt = filters; filt; filt = filt->next) - if(!strcmp(filt->filter->name, name)) - return filt->filter; + for (i = 0; registered_avfilters[i]; i++) + if (!strcmp(registered_avfilters[i]->name, name)) + return registered_avfilters[i]; return NULL; } -/* FIXME: insert in order, rather than insert at end + resort */ -void avfilter_register(AVFilter *filter) +int avfilter_register(AVFilter *filter) { - struct FilterList *newfilt = av_malloc(sizeof(struct FilterList)); + if (next_registered_avfilter_idx == MAX_REGISTERED_AVFILTERS_NB) + return -1; - newfilt->filter = filter; - newfilt->next = filters; - filters = newfilt; + registered_avfilters[next_registered_avfilter_idx++] = filter; + return 0; } -void avfilter_init(void) +AVFilter **av_filter_next(AVFilter **filter) { - avfilter_register(&vsrc_dummy); - avfilter_register(&vsrc_ppm); - avfilter_register(&vf_crop); - avfilter_register(&vf_fps); - avfilter_register(&vf_graph); - avfilter_register(&vf_graphdesc); - avfilter_register(&vf_graphfile); - avfilter_register(&vf_overlay); - avfilter_register(&vf_passthrough); - avfilter_register(&vf_rgb2bgr); - avfilter_register(&vf_slicify); - avfilter_register(&vf_vflip); + return filter ? ++filter : ®istered_avfilters[0]; } void avfilter_uninit(void) { - struct FilterList *tmp; - - for(; filters; filters = tmp) { - tmp = filters->next; - av_free(filters); - } + memset(registered_avfilters, 0, sizeof(registered_avfilters)); + next_registered_avfilter_idx = 0; } static int pad_count(const AVFilterPad *pads) { - AVFilterPad *p = (AVFilterPad *) pads; int count; - for(count = 0; p->name; count ++) p ++; + for(count = 0; pads->name; count ++) pads ++; return count; } @@ -304,78 +461,87 @@ static const char *filter_name(void *p) return filter->filter->name; } -AVFilterContext *avfilter_open(AVFilter *filter, char *inst_name) +static const AVClass avfilter_class = { + "AVFilter", + filter_name, + NULL, + LIBAVUTIL_VERSION_INT, +}; + +int avfilter_open(AVFilterContext **filter_ctx, AVFilter *filter, const char *inst_name) { - AVFilterContext *ret = av_malloc(sizeof(AVFilterContext)); + AVFilterContext *ret; + *filter_ctx = NULL; + + if (!filter) + return AVERROR(EINVAL); + + ret = av_mallocz(sizeof(AVFilterContext)); - ret->av_class = av_mallocz(sizeof(AVClass)); - ret->av_class->item_name = filter_name; + ret->av_class = &avfilter_class; ret->filter = filter; ret->name = inst_name ? av_strdup(inst_name) : NULL; ret->priv = av_mallocz(filter->priv_size); ret->input_count = pad_count(filter->inputs); - ret->input_pads = av_malloc(sizeof(AVFilterPad) * ret->input_count); - memcpy(ret->input_pads, filter->inputs, sizeof(AVFilterPad)*ret->input_count); - ret->inputs = av_mallocz(sizeof(AVFilterLink*) * ret->input_count); + if (ret->input_count) { + ret->input_pads = av_malloc(sizeof(AVFilterPad) * ret->input_count); + memcpy(ret->input_pads, filter->inputs, sizeof(AVFilterPad) * ret->input_count); + ret->inputs = av_mallocz(sizeof(AVFilterLink*) * ret->input_count); + } ret->output_count = pad_count(filter->outputs); - ret->output_pads = av_malloc(sizeof(AVFilterPad) * ret->output_count); - memcpy(ret->output_pads, filter->outputs, sizeof(AVFilterPad)*ret->output_count); - ret->outputs = av_mallocz(sizeof(AVFilterLink*) * ret->output_count); + if (ret->output_count) { + ret->output_pads = av_malloc(sizeof(AVFilterPad) * ret->output_count); + memcpy(ret->output_pads, filter->outputs, sizeof(AVFilterPad) * ret->output_count); + ret->outputs = av_mallocz(sizeof(AVFilterLink*) * ret->output_count); + } - return ret; + *filter_ctx = ret; + return 0; } void avfilter_destroy(AVFilterContext *filter) { int i; - if(filter->filter->uninit) + if (filter->filter->uninit) filter->filter->uninit(filter); - for(i = 0; i < filter->input_count; i ++) { - if(filter->inputs[i]) - filter->inputs[i]->src->outputs[filter->inputs[i]->srcpad] = NULL; - av_free(filter->inputs[i]); + for (i = 0; i < filter->input_count; i++) { + if (filter->inputs[i]) { + if (filter->inputs[i]->src) + filter->inputs[i]->src->outputs[filter->inputs[i]->srcpad] = NULL; + avfilter_formats_unref(&filter->inputs[i]->in_formats); + avfilter_formats_unref(&filter->inputs[i]->out_formats); + } + av_freep(&filter->inputs[i]); } - for(i = 0; i < filter->output_count; i ++) { - if(filter->outputs[i]) - filter->outputs[i]->dst->inputs[filter->outputs[i]->dstpad] = NULL; - av_free(filter->outputs[i]); + for (i = 0; i < filter->output_count; i++) { + if (filter->outputs[i]) { + if (filter->outputs[i]->dst) + filter->outputs[i]->dst->inputs[filter->outputs[i]->dstpad] = NULL; + avfilter_formats_unref(&filter->outputs[i]->in_formats); + avfilter_formats_unref(&filter->outputs[i]->out_formats); + } + av_freep(&filter->outputs[i]); } - av_free(filter->name); - av_free(filter->input_pads); - av_free(filter->output_pads); - av_free(filter->inputs); - av_free(filter->outputs); - av_free(filter->priv); - av_free(filter->av_class); + av_freep(&filter->name); + av_freep(&filter->input_pads); + av_freep(&filter->output_pads); + av_freep(&filter->inputs); + av_freep(&filter->outputs); + av_freep(&filter->priv); av_free(filter); } int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque) { - int ret; - - if(filter->filter->init) - if((ret = filter->filter->init(filter, args, opaque))) return ret; - return 0; -} - -int *avfilter_make_format_list(int len, ...) -{ - int *ret, i; - va_list vl; - - ret = av_malloc(sizeof(int) * (len + 1)); - va_start(vl, len); - for(i = 0; i < len; i ++) - ret[i] = va_arg(vl, int); - va_end(vl); - ret[len] = -1; + int ret=0; + if (filter->filter->init) + ret = filter->filter->init(filter, args, opaque); return ret; }