Commit 565e4993 authored by Anton Khirnov's avatar Anton Khirnov

lavfi: merge start_frame/draw_slice/end_frame

Any alleged performance benefits gained from the split are purely
mythological and do not justify added code complexity.
parent bb6c67bb
......@@ -314,7 +314,7 @@ static int output_frame(AVFilterLink *outlink, int nb_samples)
if (s->next_pts != AV_NOPTS_VALUE)
s->next_pts += nb_samples;
return ff_filter_samples(outlink, out_buf);
return ff_filter_frame(outlink, out_buf);
}
/**
......@@ -455,7 +455,7 @@ static int request_frame(AVFilterLink *outlink)
return output_frame(outlink, available_samples);
}
static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = inlink->dst;
MixContext *s = ctx->priv;
......@@ -509,7 +509,7 @@ static int init(AVFilterContext *ctx, const char *args)
snprintf(name, sizeof(name), "input%d", i);
pad.type = AVMEDIA_TYPE_AUDIO;
pad.name = av_strdup(name);
pad.filter_samples = filter_samples;
pad.filter_frame = filter_frame;
ff_insert_inpad(ctx, i, &pad);
}
......
......@@ -34,6 +34,7 @@
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct AShowInfoContext {
/**
......@@ -64,7 +65,7 @@ static void uninit(AVFilterContext *ctx)
av_freep(&s->plane_checksums);
}
static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = inlink->dst;
AShowInfoContext *s = ctx->priv;
......@@ -103,7 +104,7 @@ static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
av_log(ctx, AV_LOG_INFO, "]\n");
s->frame++;
return ff_filter_samples(inlink->dst->outputs[0], buf);
return ff_filter_frame(inlink->dst->outputs[0], buf);
}
static const AVFilterPad inputs[] = {
......@@ -112,7 +113,7 @@ static const AVFilterPad inputs[] = {
.type = AVMEDIA_TYPE_AUDIO,
.get_audio_buffer = ff_null_get_audio_buffer,
.config_props = config_input,
.filter_samples = filter_samples,
.filter_frame = filter_frame,
.min_perms = AV_PERM_READ,
},
{ NULL },
......
......@@ -39,7 +39,7 @@ typedef struct ASyncContext {
float min_delta_sec;
int max_comp;
/* set by filter_samples() to signal an output frame to request_frame() */
/* set by filter_frame() to signal an output frame to request_frame() */
int got_output;
} ASyncContext;
......@@ -141,7 +141,7 @@ static int request_frame(AVFilterLink *link)
}
buf->pts = s->pts;
return ff_filter_samples(link, buf);
return ff_filter_frame(link, buf);
}
return ret;
......@@ -161,7 +161,7 @@ static int64_t get_delay(ASyncContext *s)
return avresample_available(s->avr) + avresample_get_delay(s->avr);
}
static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = inlink->dst;
ASyncContext *s = ctx->priv;
......@@ -217,7 +217,7 @@ static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
av_samples_set_silence(buf_out->extended_data, out_size - delta,
delta, nb_channels, buf->format);
}
ret = ff_filter_samples(outlink, buf_out);
ret = ff_filter_frame(outlink, buf_out);
if (ret < 0)
goto fail;
s->got_output = 1;
......@@ -243,7 +243,7 @@ static const AVFilterPad avfilter_af_asyncts_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_samples = filter_samples
.filter_frame = filter_frame,
},
{ NULL }
};
......
......@@ -313,7 +313,7 @@ static int channelmap_query_formats(AVFilterContext *ctx)
return 0;
}
static int channelmap_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
static int channelmap_filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
......@@ -355,7 +355,7 @@ static int channelmap_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *bu
memcpy(buf->data, buf->extended_data,
FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0]));
return ff_filter_samples(outlink, buf);
return ff_filter_frame(outlink, buf);
}
static int channelmap_config_input(AVFilterLink *inlink)
......@@ -389,7 +389,7 @@ static const AVFilterPad avfilter_af_channelmap_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_samples = channelmap_filter_samples,
.filter_frame = channelmap_filter_frame,
.config_props = channelmap_config_input
},
{ NULL }
......
......@@ -111,7 +111,7 @@ static int query_formats(AVFilterContext *ctx)
return 0;
}
static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = inlink->dst;
int i, ret = 0;
......@@ -128,7 +128,7 @@ static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
buf_out->audio->channel_layout =
av_channel_layout_extract_channel(buf->audio->channel_layout, i);
ret = ff_filter_samples(ctx->outputs[i], buf_out);
ret = ff_filter_frame(ctx->outputs[i], buf_out);
if (ret < 0)
break;
}
......@@ -140,7 +140,7 @@ static const AVFilterPad avfilter_af_channelsplit_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_samples = filter_samples,
.filter_frame = filter_frame,
},
{ NULL }
};
......
......@@ -93,7 +93,7 @@ static const AVClass join_class = {
.version = LIBAVUTIL_VERSION_INT,
};
static int filter_samples(AVFilterLink *link, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *link, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = link->dst;
JoinContext *s = ctx->priv;
......@@ -230,7 +230,7 @@ static int join_init(AVFilterContext *ctx, const char *args)
snprintf(name, sizeof(name), "input%d", i);
pad.type = AVMEDIA_TYPE_AUDIO;
pad.name = av_strdup(name);
pad.filter_samples = filter_samples;
pad.filter_frame = filter_frame;
pad.needs_fifo = 1;
......@@ -471,7 +471,7 @@ static int join_request_frame(AVFilterLink *outlink)
priv->nb_in_buffers = ctx->nb_inputs;
buf->buf->priv = priv;
ret = ff_filter_samples(outlink, buf);
ret = ff_filter_frame(outlink, buf);
memset(s->input_frames, 0, sizeof(*s->input_frames) * ctx->nb_inputs);
......
......@@ -40,7 +40,7 @@ typedef struct ResampleContext {
int64_t next_pts;
/* set by filter_samples() to signal an output frame to request_frame() */
/* set by filter_frame() to signal an output frame to request_frame() */
int got_output;
} ResampleContext;
......@@ -162,12 +162,12 @@ static int request_frame(AVFilterLink *outlink)
}
buf->pts = s->next_pts;
return ff_filter_samples(outlink, buf);
return ff_filter_frame(outlink, buf);
}
return ret;
}
static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
AVFilterContext *ctx = inlink->dst;
ResampleContext *s = ctx->priv;
......@@ -224,7 +224,7 @@ static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
s->next_pts = buf_out->pts + buf_out->audio->nb_samples;
ret = ff_filter_samples(outlink, buf_out);
ret = ff_filter_frame(outlink, buf_out);
s->got_output = 1;
}
......@@ -232,7 +232,7 @@ fail:
avfilter_unref_buffer(buf);
} else {
buf->format = outlink->format;
ret = ff_filter_samples(outlink, buf);
ret = ff_filter_frame(outlink, buf);
s->got_output = 1;
}
......@@ -243,7 +243,7 @@ static const AVFilterPad avfilter_af_resample_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_samples = filter_samples,
.filter_frame = filter_frame,
.min_perms = AV_PERM_READ
},
{ NULL }
......
......@@ -20,7 +20,7 @@
#include "avfilter.h"
#include "internal.h"
static int null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
static int null_filter_frame(AVFilterLink *link, AVFilterBufferRef *samplesref)
{
avfilter_unref_bufferp(&samplesref);
return 0;
......@@ -30,7 +30,7 @@ static const AVFilterPad avfilter_asink_anullsink_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_samples = null_filter_samples,
.filter_frame = null_filter_frame,
},
{ NULL },
};
......
......@@ -146,50 +146,3 @@ fail:
av_freep(&samples);
return NULL;
}
static int default_filter_samples(AVFilterLink *link,
AVFilterBufferRef *samplesref)
{
return ff_filter_samples(link->dst->outputs[0], samplesref);
}
int ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
{
int (*filter_samples)(AVFilterLink *, AVFilterBufferRef *);
AVFilterPad *dst = link->dstpad;
AVFilterBufferRef *buf_out;
FF_DPRINTF_START(NULL, filter_samples); ff_dlog_link(NULL, link, 1);
if (!(filter_samples = dst->filter_samples))
filter_samples = 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->dstpad->min_perms, link->dstpad->rej_perms);
buf_out = ff_default_get_audio_buffer(link, dst->min_perms,
samplesref->audio->nb_samples);
if (!buf_out) {
avfilter_unref_buffer(samplesref);
return AVERROR(ENOMEM);
}
buf_out->pts = samplesref->pts;
buf_out->audio->sample_rate = samplesref->audio->sample_rate;
/* Copy actual data into new samples buffer */
av_samples_copy(buf_out->extended_data, samplesref->extended_data,
0, 0, samplesref->audio->nb_samples,
av_get_channel_layout_nb_channels(link->channel_layout),
link->format);
avfilter_unref_buffer(samplesref);
} else
buf_out = samplesref;
return filter_samples(link, buf_out);
}
......@@ -42,17 +42,4 @@ AVFilterBufferRef *ff_null_get_audio_buffer(AVFilterLink *link, int perms,
AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms,
int nb_samples);
/**
* Send a buffer of audio samples to the next filter.
*
* @param link the output link over which the audio samples are being sent
* @param samplesref a reference to the buffer of audio samples being sent. The
* receiving filter will free this reference when it no longer
* needs it or pass it on to the next filter.
*
* @return >= 0 on success, a negative AVERROR on error. The receiving filter
* is responsible for unreferencing samplesref in case of error.
*/
int ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref);
#endif /* AVFILTER_AUDIO_H */
......@@ -23,12 +23,16 @@
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/imgutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/rational.h"
#include "libavutil/samplefmt.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
unsigned avfilter_version(void) {
return LIBAVFILTER_VERSION_INT;
......@@ -446,3 +450,68 @@ enum AVMediaType avfilter_pad_get_type(AVFilterPad *pads, int pad_idx)
{
return pads[pad_idx].type;
}
static int default_filter_frame(AVFilterLink *link, AVFilterBufferRef *frame)
{
return ff_filter_frame(link->dst->outputs[0], frame);
}
int ff_filter_frame(AVFilterLink *link, AVFilterBufferRef *frame)
{
int (*filter_frame)(AVFilterLink *, AVFilterBufferRef *);
AVFilterPad *dst = link->dstpad;
AVFilterBufferRef *out;
int perms = frame->perms;
FF_DPRINTF_START(NULL, filter_frame);
ff_dlog_link(NULL, link, 1);
if (!(filter_frame = dst->filter_frame))
filter_frame = default_filter_frame;
if (frame->linesize[0] < 0)
perms |= AV_PERM_NEG_LINESIZES;
/* prepare to copy the frame if the buffer has insufficient permissions */
if ((dst->min_perms & perms) != dst->min_perms ||
dst->rej_perms & perms) {
av_log(link->dst, AV_LOG_DEBUG,
"Copying data in avfilter (have perms %x, need %x, reject %x)\n",
perms, link->dstpad->min_perms, link->dstpad->rej_perms);
switch (link->type) {
case AVMEDIA_TYPE_VIDEO:
out = ff_get_video_buffer(link, dst->min_perms,
link->w, link->h);
break;
case AVMEDIA_TYPE_AUDIO:
out = ff_get_audio_buffer(link, dst->min_perms,
frame->audio->nb_samples);
break;
default: return AVERROR(EINVAL);
}
if (!out) {
avfilter_unref_buffer(frame);
return AVERROR(ENOMEM);
}
avfilter_copy_buffer_ref_props(out, frame);
switch (link->type) {
case AVMEDIA_TYPE_VIDEO:
av_image_copy(out->data, out->linesize, frame->data, frame->linesize,
frame->format, frame->video->w, frame->video->h);
break;
case AVMEDIA_TYPE_AUDIO:
av_samples_copy(out->extended_data, frame->extended_data,
0, 0, frame->audio->nb_samples,
av_get_channel_layout_nb_channels(frame->audio->channel_layout),
frame->format);
break;
default: return AVERROR(EINVAL);
}
avfilter_unref_buffer(frame);
} else
out = frame;
return filter_frame(link, out);
}
......@@ -253,14 +253,7 @@ struct AVFilterPad {
int rej_perms;
/**
* Callback called before passing the first slice of a new frame. If
* NULL, the filter layer will default to storing a reference to the
* picture inside the link structure.
*
* Input video pads only.
*
* @return >= 0 on success, a negative AVERROR on error. picref will be
* unreferenced by the caller in case of error.
* @deprecated unused
*/
int (*start_frame)(AVFilterLink *link, AVFilterBufferRef *picref);
......@@ -282,37 +275,26 @@ struct AVFilterPad {
int nb_samples);
/**
* Callback called after the slices of a frame are completely sent. If
* NULL, the filter layer will default to releasing the reference stored
* in the link structure during start_frame().
*
* Input video pads only.
*
* @return >= 0 on success, a negative AVERROR on error.
* @deprecated unused
*/
int (*end_frame)(AVFilterLink *link);
/**
* Slice drawing callback. This is where a filter receives video data
* and should do its processing.
*
* Input video pads only.
*
* @return >= 0 on success, a negative AVERROR on error.
* @deprecated unused
*/
int (*draw_slice)(AVFilterLink *link, int y, int height, int slice_dir);
/**
* Samples filtering callback. This is where a filter receives audio data
* and should do its processing.
* Filtering callback. This is where a filter receives a frame with
* audio/video data and should do its processing.
*
* Input audio pads only.
* Input pads only.
*
* @return >= 0 on success, a negative AVERROR on error. This function
* must ensure that samplesref is properly unreferenced on error if it
* hasn't been passed on to another filter.
*/
int (*filter_samples)(AVFilterLink *link, AVFilterBufferRef *samplesref);
int (*filter_frame)(AVFilterLink *link, AVFilterBufferRef *frame);
/**
* Frame poll callback. This returns the number of immediately available
......@@ -531,18 +513,6 @@ struct AVFilterLink {
AVLINK_STARTINIT, ///< started, but incomplete
AVLINK_INIT ///< complete
} init_state;
/**
* The buffer reference currently being sent across the link by the source
* filter. This is used internally by the filter system to allow
* automatic copying of buffers which do not have sufficient permissions
* for the destination. This should not be accessed directly by the
* filters.
*/
AVFilterBufferRef *src_buf;
AVFilterBufferRef *cur_buf;
AVFilterBufferRef *out_buf;
};
/**
......
......@@ -48,13 +48,12 @@ static av_cold void uninit(AVFilterContext *ctx)
av_audio_fifo_free(sink->audio_fifo);
}
static int start_frame(AVFilterLink *link, AVFilterBufferRef *buf)
static int filter_frame(AVFilterLink *link, AVFilterBufferRef *buf)
{
BufferSinkContext *s = link->dst->priv;
av_assert0(!s->cur_buf);
s->cur_buf = buf;
link->cur_buf = NULL;
return 0;
}
......@@ -144,7 +143,7 @@ static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.start_frame = start_frame,
.filter_frame = filter_frame,
.min_perms = AV_PERM_READ,
.needs_fifo = 1
},
......@@ -165,7 +164,7 @@ static const AVFilterPad avfilter_asink_abuffer_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_samples = start_frame,
.filter_frame = filter_frame,
.min_perms = AV_PERM_READ,
.needs_fifo = 1
},
......
......@@ -327,20 +327,7 @@ static int request_frame(AVFilterLink *link)
}
av_fifo_generic_read(c->fifo, &buf, sizeof(buf), NULL);
switch (link->type) {
case AVMEDIA_TYPE_VIDEO:
if ((ret = ff_start_frame(link, buf)) < 0 ||
(ret = ff_draw_slice(link, 0, link->h, 1)) < 0 ||
(ret = ff_end_frame(link)) < 0)
return ret;
break;
case AVMEDIA_TYPE_AUDIO:
ret = ff_filter_samples(link, buf);
break;
default:
avfilter_unref_bufferp(&buf);
return AVERROR(EINVAL);
}
ff_filter_frame(link, buf);
return ret;
}
......
......@@ -77,7 +77,6 @@ static int add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf)
{
FifoContext *fifo = inlink->dst->priv;
inlink->cur_buf = NULL;
fifo->last->next = av_mallocz(sizeof(Buf));
if (!fifo->last->next) {
avfilter_unref_buffer(buf);
......@@ -99,16 +98,6 @@ static void queue_pop(FifoContext *s)
s->root.next = tmp;
}
static int end_frame(AVFilterLink *inlink)
{
return 0;
}
static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
{
return 0;
}
/**
* Move data pointers and pts offset samples forward.
*/
......@@ -228,7 +217,7 @@ static int return_audio_frame(AVFilterContext *ctx)
buf_out = s->buf_out;
s->buf_out = NULL;
}
return ff_filter_samples(link, buf_out);
return ff_filter_frame(link, buf_out);
}
static int request_frame(AVFilterLink *outlink)
......@@ -241,27 +230,11 @@ static int request_frame(AVFilterLink *outlink)
return ret;
}
/* by doing this, we give ownership of the reference to the next filter,
* so we don't have to worry about dereferencing it ourselves. */
switch (outlink->type) {
case AVMEDIA_TYPE_VIDEO:
if ((ret = ff_start_frame(outlink, fifo->root.next->buf)) < 0 ||
(ret = ff_draw_slice(outlink, 0, outlink->h, 1)) < 0 ||
(ret = ff_end_frame(outlink)) < 0)
return ret;
if (outlink->request_samples) {
return return_audio_frame(outlink->src);
} else {
ret = ff_filter_frame(outlink, fifo->root.next->buf);
queue_pop(fifo);
break;
case AVMEDIA_TYPE_AUDIO:
if (outlink->request_samples) {
return return_audio_frame(outlink->src);
} else {
ret = ff_filter_samples(outlink, fifo->root.next->buf);
queue_pop(fifo);
}
break;
default:
return AVERROR(EINVAL);
}
return ret;
......@@ -272,9 +245,7 @@ static const AVFilterPad avfilter_vf_fifo_inputs[] = {
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.get_video_buffer = ff_null_get_video_buffer,
.start_frame = add_to_queue,
.draw_slice = draw_slice,
.end_frame = end_frame,
.filter_frame = add_to_queue,
.rej_perms = AV_PERM_REUSE2,
},
{ NULL }
......@@ -307,7 +278,7 @@ static const AVFilterPad avfilter_af_afifo_inputs[] = {
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.get_audio_buffer = ff_null_get_audio_buffer,
.filter_samples = add_to_queue,
.filter_frame = add_to_queue,
.rej_perms = AV_PERM_REUSE2,
},
{ NULL }
......
......@@ -63,18 +63,6 @@ struct AVFilterPad {
*/
int rej_perms;
/**
* Callback called before passing the first slice of a new frame. If
* NULL, the filter layer will default to storing a reference to the
* picture inside the link structure.
*
* Input video pads only.
*
* @return >= 0 on success, a negative AVERROR on error. picref will be
* unreferenced by the caller in case of error.
*/
void (*start_frame)(AVFilterLink *link, AVFilterBufferRef *picref);
/**
* Callback function to get a video buffer. If NULL, the filter system will
* use avfilter_default_get_video_buffer().
......@@ -93,37 +81,16 @@ struct AVFilterPad {
int nb_samples);
/**
* Callback called after the slices of a frame are completely sent. If
* NULL, the filter layer will default to releasing the reference stored
* in the link structure during start_frame().
* Filtering callback. This is where a filter receives a frame with
* audio/video data and should do its processing.
*
* Input video pads only.
*
* @return >= 0 on success, a negative AVERROR on error.
*/
int (*end_frame)(AVFilterLink *link);
/**
* Slice drawing callback. This is where a filter receives video data
* and should do its processing.
*
* Input video pads only.
*
* @return >= 0 on success, a negative AVERROR on error.
*/
int (*draw_slice)(AVFilterLink *link, int y, int height, int slice_dir);
/**
* Samples filtering callback. This is where a filter receives audio data
* and should do its processing.
*
* Input audio pads only.
* Input pads only.
*
* @return >= 0 on success, a negative AVERROR on error. This function
* must ensure that samplesref is properly unreferenced on error if it
* hasn't been passed on to another filter.
*/
int (*filter_samples)(AVFilterLink *link, AVFilterBufferRef *samplesref);
int (*filter_frame)(AVFilterLink *link, AVFilterBufferRef *frame);
/**
* Frame poll callback. This returns the number of immediately available
......@@ -237,4 +204,17 @@ int ff_poll_frame(AVFilterLink *link);
*/
int ff_request_frame(AVFilterLink *link);
/**
* Send a frame of data to the next filter.
*
* @param link the output link over which the data is being sent
* @param frame a reference to the buffer of data being sent. The
* receiving filter will free this reference when it no longer
* needs it or pass it on to the next filter.
*
* @return >= 0 on success, a negative AVERROR on error. The receiving filter
* is responsible for unreferencing frame in case of error.
*/
int ff_filter_frame(AVFilterLink *link, AVFilterBufferRef *frame);
#endif /* AVFILTER_INTERNAL_H */
......@@ -67,46 +67,23 @@ static void split_uninit(AVFilterContext *ctx)
av_freep(&ctx->output_pads[i].name);
}
static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
{
AVFilterContext *ctx = inlink->dst;
int i, ret = 0;
for (i = 0; i < ctx->nb_outputs; i++) {
AVFilterBufferRef *buf_out = avfilter_ref_buffer(picref, ~AV_PERM_WRITE);
if (!buf_out)
return AVERROR(ENOMEM);
ret = ff_start_frame(ctx->outputs[i], buf_out);
if (ret < 0)