Merge commit 'a5e8c41c28f907d98d2a739db08f7aef4cbfcf3a'
authorMichael Niedermayer <michaelni@gmx.at>
Tue, 26 Jun 2012 21:27:59 +0000 (23:27 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Tue, 26 Jun 2012 21:57:07 +0000 (23:57 +0200)
* commit 'a5e8c41c28f907d98d2a739db08f7aef4cbfcf3a':
  lavfi: remove 'opaque' parameter from AVFilter.init()
  mov: do not try to read total disc/track number if data atom is too short.
  avconv: fix -force_key_frames
  dxva2_h264: fix signaling of mbaff frames
  x86: fft: elf64: fix PIC build

Conflicts:
ffmpeg.c
libavcodec/v210dec.h
libavfilter/asrc_anullsrc.c
libavfilter/buffersrc.c
libavfilter/src_movie.c
libavfilter/vf_drawtext.c
libavfilter/vf_fade.c
libavfilter/vf_overlay.c
libavfilter/vsrc_color.c
libavfilter/vsrc_testsrc.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>
64 files changed:
1  2 
ffmpeg.c
libavfilter/af_aconvert.c
libavfilter/af_aformat.c
libavfilter/af_amerge.c
libavfilter/af_amix.c
libavfilter/af_aresample.c
libavfilter/af_asetnsamples.c
libavfilter/af_ashowinfo.c
libavfilter/af_astreamsync.c
libavfilter/af_asyncts.c
libavfilter/af_atempo.c
libavfilter/af_channelmap.c
libavfilter/af_channelsplit.c
libavfilter/af_pan.c
libavfilter/af_silencedetect.c
libavfilter/af_volume.c
libavfilter/asrc_aevalsrc.c
libavfilter/asrc_anullsrc.c
libavfilter/avfilter.c
libavfilter/avfilter.h
libavfilter/buffersrc.c
libavfilter/fifo.c
libavfilter/sink_buffer.c
libavfilter/split.c
libavfilter/src_movie.c
libavfilter/vf_aspect.c
libavfilter/vf_ass.c
libavfilter/vf_bbox.c
libavfilter/vf_blackdetect.c
libavfilter/vf_blackframe.c
libavfilter/vf_boxblur.c
libavfilter/vf_colormatrix.c
libavfilter/vf_crop.c
libavfilter/vf_cropdetect.c
libavfilter/vf_delogo.c
libavfilter/vf_deshake.c
libavfilter/vf_drawbox.c
libavfilter/vf_drawtext.c
libavfilter/vf_fade.c
libavfilter/vf_fieldorder.c
libavfilter/vf_format.c
libavfilter/vf_fps.c
libavfilter/vf_frei0r.c
libavfilter/vf_gradfun.c
libavfilter/vf_hqdn3d.c
libavfilter/vf_idet.c
libavfilter/vf_libopencv.c
libavfilter/vf_lut.c
libavfilter/vf_mp.c
libavfilter/vf_overlay.c
libavfilter/vf_pad.c
libavfilter/vf_removelogo.c
libavfilter/vf_scale.c
libavfilter/vf_select.c
libavfilter/vf_setfield.c
libavfilter/vf_setpts.c
libavfilter/vf_settb.c
libavfilter/vf_showinfo.c
libavfilter/vf_slicify.c
libavfilter/vf_transpose.c
libavfilter/vf_unsharp.c
libavfilter/vf_yadif.c
libavfilter/vsrc_color.c
libavfilter/vsrc_testsrc.c

diff --cc ffmpeg.c
+++ b/ffmpeg.c
@@@ -306,11 -270,8 +306,12 @@@ typedef struct OutputStream 
      int64_t *forced_kf_pts;
      int forced_kf_count;
      int forced_kf_index;
+     char *forced_keyframes;
  
 +    /* audio only */
 +    int audio_channels_map[SWR_CH_MAX];  /* list of the channels id to pick from the source stream */
 +    int audio_channels_mapped;           /* number of channels in audio_channels_map */
 +
      FILE *logfile;
  
      OutputFilter *filter;
@@@ -1383,8 -1063,9 +1384,9 @@@ void av_noreturn exit_program(int ret
          }
          output_streams[i]->bitstream_filters = NULL;
  
 -        av_freep(&output_streams[i]->avfilter);
+         av_freep(&output_streams[i]->forced_keyframes);
          av_freep(&output_streams[i]->filtered_frame);
 +        av_freep(&output_streams[i]->avfilter);
          av_freep(&output_streams[i]);
      }
      for (i = 0; i < nb_input_files; i++) {
@@@ -2990,13 -2547,15 +3015,16 @@@ static int transcode_init(void
                      ost->filter->filter->inputs[0]->sample_aspect_ratio;
                  codec->pix_fmt = ost->filter->filter->inputs[0]->format;
  
 -                if (codec->width   != icodec->width  ||
 +                if (!icodec ||
 +                    codec->width   != icodec->width  ||
                      codec->height  != icodec->height ||
                      codec->pix_fmt != icodec->pix_fmt) {
 -                    codec->bits_per_raw_sample = 0;
 +                    codec->bits_per_raw_sample = frame_bits_per_raw_sample;
                  }
  
+                 if (ost->forced_keyframes)
+                     parse_forced_key_frames(ost->forced_keyframes, ost,
+                                             ost->st->codec);
                  break;
              case AVMEDIA_TYPE_SUBTITLE:
                  codec->time_base = (AVRational){1, 1000};
@@@ -4588,15 -3868,9 +4593,15 @@@ static OutputStream *new_video_stream(O
      st  = ost->st;
      video_enc = st->codec;
  
 +    MATCH_PER_STREAM_OPT(frame_rates, str, frame_rate, oc, st);
 +    if (frame_rate && av_parse_video_rate(&ost->frame_rate, frame_rate) < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate);
 +        exit_program(1);
 +    }
 +
      if (!ost->stream_copy) {
          const char *p = NULL;
-         char *forced_key_frames = NULL, *frame_size = NULL;
 -        char *frame_rate = NULL, *frame_size = NULL;
++        char *frame_size = NULL;
          char *frame_aspect_ratio = NULL, *frame_pix_fmt = NULL;
          char *intra_matrix = NULL, *inter_matrix = NULL;
          const char *filters = "null";
index d5bc6a8,0000000..51167f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,172 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
 +/*
 + * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram <smeenaks@ucsd.edu>
 + * Copyright (c) 2011 Stefano Sabatini
 + * Copyright (c) 2011 Mina Nagy Zaki
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * sample format and channel layout conversion audio filter
 + */
 +
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/avstring.h"
 +#include "libswresample/swresample.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    enum AVSampleFormat  out_sample_fmt;
 +    int64_t              out_chlayout;
 +    struct SwrContext *swr;
 +} AConvertContext;
 +
++static av_cold int init(AVFilterContext *ctx, const char *args0)
 +{
 +    AConvertContext *aconvert = ctx->priv;
 +    char *arg, *ptr = NULL;
 +    int ret = 0;
 +    char *args = av_strdup(args0);
 +
 +    aconvert->out_sample_fmt  = AV_SAMPLE_FMT_NONE;
 +    aconvert->out_chlayout    = 0;
 +
 +    if ((arg = av_strtok(args, ":", &ptr)) && strcmp(arg, "auto")) {
 +        if ((ret = ff_parse_sample_format(&aconvert->out_sample_fmt, arg, ctx)) < 0)
 +            goto end;
 +    }
 +    if ((arg = av_strtok(NULL, ":", &ptr)) && strcmp(arg, "auto")) {
 +        if ((ret = ff_parse_channel_layout(&aconvert->out_chlayout, arg, ctx)) < 0)
 +            goto end;
 +    }
 +
 +end:
 +    av_freep(&args);
 +    return ret;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AConvertContext *aconvert = ctx->priv;
 +    swr_free(&aconvert->swr);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats = NULL;
 +    AConvertContext *aconvert = ctx->priv;
 +    AVFilterLink *inlink  = ctx->inputs[0];
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    AVFilterChannelLayouts *layouts;
 +
 +    ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO),
 +                         &inlink->out_formats);
 +    if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) {
 +        formats = NULL;
 +        ff_add_format(&formats, aconvert->out_sample_fmt);
 +        ff_formats_ref(formats, &outlink->in_formats);
 +    } else
 +        ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO),
 +                             &outlink->in_formats);
 +
 +    ff_channel_layouts_ref(ff_all_channel_layouts(),
 +                         &inlink->out_channel_layouts);
 +    if (aconvert->out_chlayout != 0) {
 +        layouts = NULL;
 +        ff_add_channel_layout(&layouts, aconvert->out_chlayout);
 +        ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
 +    } else
 +        ff_channel_layouts_ref(ff_all_channel_layouts(),
 +                             &outlink->in_channel_layouts);
 +
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    int ret;
 +    AVFilterContext *ctx = outlink->src;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    AConvertContext *aconvert = ctx->priv;
 +    char buf1[64], buf2[64];
 +
 +    /* if not specified in args, use the format and layout of the output */
 +    if (aconvert->out_sample_fmt == AV_SAMPLE_FMT_NONE)
 +        aconvert->out_sample_fmt = outlink->format;
 +    if (aconvert->out_chlayout   == 0)
 +        aconvert->out_chlayout   = outlink->channel_layout;
 +
 +    aconvert->swr = swr_alloc_set_opts(aconvert->swr,
 +                                       aconvert->out_chlayout, aconvert->out_sample_fmt, inlink->sample_rate,
 +                                       inlink->channel_layout, inlink->format,           inlink->sample_rate,
 +                                       0, ctx);
 +    if (!aconvert->swr)
 +        return AVERROR(ENOMEM);
 +    ret = swr_init(aconvert->swr);
 +    if (ret < 0)
 +        return ret;
 +
 +    av_get_channel_layout_string(buf1, sizeof(buf1),
 +                                 -1, inlink ->channel_layout);
 +    av_get_channel_layout_string(buf2, sizeof(buf2),
 +                                 -1, outlink->channel_layout);
 +    av_log(ctx, AV_LOG_INFO,
 +           "fmt:%s cl:%s -> fmt:%s cl:%s\n",
 +           av_get_sample_fmt_name(inlink ->format), buf1,
 +           av_get_sample_fmt_name(outlink->format), buf2);
 +
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamplesref)
 +{
 +    AConvertContext *aconvert = inlink->dst->priv;
 +    const int n = insamplesref->audio->nb_samples;
 +    AVFilterLink *const outlink = inlink->dst->outputs[0];
 +    AVFilterBufferRef *outsamplesref = ff_get_audio_buffer(outlink, AV_PERM_WRITE, n);
 +
 +    swr_convert(aconvert->swr, outsamplesref->data, n,
 +                        (void *)insamplesref->data, n);
 +
 +    avfilter_copy_buffer_ref_props(outsamplesref, insamplesref);
 +    outsamplesref->audio->channel_layout = outlink->channel_layout;
 +
 +    ff_filter_samples(outlink, outsamplesref);
 +    avfilter_unref_buffer(insamplesref);
 +}
 +
 +AVFilter avfilter_af_aconvert = {
 +    .name          = "aconvert",
 +    .description   = NULL_IF_CONFIG_SMALL("Convert the input audio to sample_fmt:channel_layout."),
 +    .priv_size     = sizeof(AConvertContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .type            = AVMEDIA_TYPE_AUDIO,
 +                                    .filter_samples  = filter_samples,
 +                                    .min_perms       = AV_PERM_READ, },
 +                                  { .name = NULL}},
 +    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .type            = AVMEDIA_TYPE_AUDIO,
 +                                    .config_props    = config_output, },
 +                                  { .name = NULL}},
 +};
Simple merge
index 1d45856,0000000..429057c
mode 100644,000000..100644
--- /dev/null
@@@ -1,337 -1,0 +1,337 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Audio merging filter
 + */
 +
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/bprint.h"
 +#include "libavutil/opt.h"
 +#include "libswresample/swresample.h" // only for SWR_CH_MAX
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "bufferqueue.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int nb_inputs;
 +    int route[SWR_CH_MAX]; /**< channels routing, see copy_samples */
 +    int bps;
 +    struct amerge_input {
 +        struct FFBufQueue queue;
 +        int nb_ch;         /**< number of channels for the input */
 +        int nb_samples;
 +        int pos;
 +    } *in;
 +} AMergeContext;
 +
 +#define OFFSET(x) offsetof(AMergeContext, x)
 +
 +static const AVOption amerge_options[] = {
 +    { "inputs", "specify the number of inputs", OFFSET(nb_inputs),
 +      AV_OPT_TYPE_INT, { .dbl = 2 }, 2, SWR_CH_MAX },
 +    {0}
 +};
 +
 +AVFILTER_DEFINE_CLASS(amerge);
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AMergeContext *am = ctx->priv;
 +    int i;
 +
 +    for (i = 0; i < am->nb_inputs; i++)
 +        ff_bufqueue_discard_all(&am->in[i].queue);
 +    av_freep(&am->in);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AMergeContext *am = ctx->priv;
 +    int64_t inlayout[SWR_CH_MAX], outlayout = 0;
 +    AVFilterFormats *formats;
 +    AVFilterChannelLayouts *layouts;
 +    int i, overlap = 0, nb_ch = 0;
 +
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        if (!ctx->inputs[i]->in_channel_layouts ||
 +            !ctx->inputs[i]->in_channel_layouts->nb_channel_layouts) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "No channel layout for input %d\n", i + 1);
 +            return AVERROR(EINVAL);
 +        }
 +        inlayout[i] = ctx->inputs[i]->in_channel_layouts->channel_layouts[0];
 +        if (ctx->inputs[i]->in_channel_layouts->nb_channel_layouts > 1) {
 +            char buf[256];
 +            av_get_channel_layout_string(buf, sizeof(buf), 0, inlayout[i]);
 +            av_log(ctx, AV_LOG_INFO, "Using \"%s\" for input %d\n", buf, i + 1);
 +        }
 +        am->in[i].nb_ch = av_get_channel_layout_nb_channels(inlayout[i]);
 +        if (outlayout & inlayout[i])
 +            overlap++;
 +        outlayout |= inlayout[i];
 +        nb_ch += am->in[i].nb_ch;
 +    }
 +    if (nb_ch > SWR_CH_MAX) {
 +        av_log(ctx, AV_LOG_ERROR, "Too many channels (max %d)\n", SWR_CH_MAX);
 +        return AVERROR(EINVAL);
 +    }
 +    if (overlap) {
 +        av_log(ctx, AV_LOG_WARNING,
 +               "Inputs overlap: output layout will be meaningless\n");
 +        for (i = 0; i < nb_ch; i++)
 +            am->route[i] = i;
 +        outlayout = av_get_default_channel_layout(nb_ch);
 +        if (!outlayout)
 +            outlayout = ((int64_t)1 << nb_ch) - 1;
 +    } else {
 +        int *route[SWR_CH_MAX];
 +        int c, out_ch_number = 0;
 +
 +        route[0] = am->route;
 +        for (i = 1; i < am->nb_inputs; i++)
 +            route[i] = route[i - 1] + am->in[i - 1].nb_ch;
 +        for (c = 0; c < 64; c++)
 +            for (i = 0; i < am->nb_inputs; i++)
 +                if ((inlayout[i] >> c) & 1)
 +                    *(route[i]++) = out_ch_number++;
 +    }
 +    formats = ff_make_format_list(ff_packed_sample_fmts_array);
 +    ff_set_common_formats(ctx, formats);
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        layouts = NULL;
 +        ff_add_channel_layout(&layouts, inlayout[i]);
 +        ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts);
 +    }
 +    layouts = NULL;
 +    ff_add_channel_layout(&layouts, outlayout);
 +    ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts);
 +    ff_set_common_samplerates(ctx, ff_all_samplerates());
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AMergeContext *am = ctx->priv;
 +    AVBPrint bp;
 +    int i;
 +
 +    for (i = 1; i < am->nb_inputs; i++) {
 +        if (ctx->inputs[i]->sample_rate != ctx->inputs[0]->sample_rate) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Inputs must have the same sample rate "
 +                   "(%"PRIi64" for in%d vs %"PRIi64")\n",
 +                   ctx->inputs[i]->sample_rate, i, ctx->inputs[0]->sample_rate);
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +    am->bps = av_get_bytes_per_sample(ctx->outputs[0]->format);
 +    outlink->sample_rate = ctx->inputs[0]->sample_rate;
 +    outlink->time_base   = ctx->inputs[0]->time_base;
 +
 +    av_bprint_init(&bp, 0, 1);
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        av_bprintf(&bp, "%sin%d:", i ? " + " : "", i);
 +        av_bprint_channel_layout(&bp, -1, ctx->inputs[i]->channel_layout);
 +    }
 +    av_bprintf(&bp, " -> out:");
 +    av_bprint_channel_layout(&bp, -1, ctx->outputs[0]->channel_layout);
 +    av_log(ctx, AV_LOG_INFO, "%s\n", bp.str);
 +
 +    return 0;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AMergeContext *am = ctx->priv;
 +    int i, ret;
 +
 +    for (i = 0; i < am->nb_inputs; i++)
 +        if (!am->in[i].nb_samples)
 +            if ((ret = ff_request_frame(ctx->inputs[i])) < 0)
 +                return ret;
 +    return 0;
 +}
 +
 +/**
 + * Copy samples from several input streams to one output stream.
 + * @param nb_inputs number of inputs
 + * @param in        inputs; used only for the nb_ch field;
 + * @param route     routing values;
 + *                  input channel i goes to output channel route[i];
 + *                  i <  in[0].nb_ch are the channels from the first output;
 + *                  i >= in[0].nb_ch are the channels from the second output
 + * @param ins       pointer to the samples of each inputs, in packed format;
 + *                  will be left at the end of the copied samples
 + * @param outs      pointer to the samples of the output, in packet format;
 + *                  must point to a buffer big enough;
 + *                  will be left at the end of the copied samples
 + * @param ns        number of samples to copy
 + * @param bps       bytes per sample
 + */
 +static inline void copy_samples(int nb_inputs, struct amerge_input in[],
 +                                int *route, uint8_t *ins[],
 +                                uint8_t **outs, int ns, int bps)
 +{
 +    int *route_cur;
 +    int i, c, nb_ch = 0;
 +
 +    for (i = 0; i < nb_inputs; i++)
 +        nb_ch += in[i].nb_ch;
 +    while (ns--) {
 +        route_cur = route;
 +        for (i = 0; i < nb_inputs; i++) {
 +            for (c = 0; c < in[i].nb_ch; c++) {
 +                memcpy((*outs) + bps * *(route_cur++), ins[i], bps);
 +                ins[i] += bps;
 +            }
 +        }
 +        *outs += nb_ch * bps;
 +    }
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AMergeContext *am = ctx->priv;
 +    AVFilterLink *const outlink = ctx->outputs[0];
 +    int input_number;
 +    int nb_samples, ns, i;
 +    AVFilterBufferRef *outbuf, *inbuf[SWR_CH_MAX];
 +    uint8_t *ins[SWR_CH_MAX], *outs;
 +
 +    for (input_number = 0; input_number < am->nb_inputs; input_number++)
 +        if (inlink == ctx->inputs[input_number])
 +            break;
 +    av_assert1(input_number < am->nb_inputs);
 +    ff_bufqueue_add(ctx, &am->in[input_number].queue, insamples);
 +    am->in[input_number].nb_samples += insamples->audio->nb_samples;
 +    nb_samples = am->in[0].nb_samples;
 +    for (i = 1; i < am->nb_inputs; i++)
 +        nb_samples = FFMIN(nb_samples, am->in[i].nb_samples);
 +    if (!nb_samples)
 +        return;
 +
 +    outbuf = ff_get_audio_buffer(ctx->outputs[0], AV_PERM_WRITE, nb_samples);
 +    outs = outbuf->data[0];
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        inbuf[i] = ff_bufqueue_peek(&am->in[i].queue, 0);
 +        ins[i] = inbuf[i]->data[0] +
 +                 am->in[i].pos * am->in[i].nb_ch * am->bps;
 +    }
 +    outbuf->pts = inbuf[0]->pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE :
 +                  inbuf[0]->pts +
 +                  av_rescale_q(am->in[0].pos,
 +                               (AVRational){ 1, ctx->inputs[0]->sample_rate },
 +                               ctx->outputs[0]->time_base);
 +
 +    avfilter_copy_buffer_ref_props(outbuf, inbuf[0]);
 +    outbuf->audio->nb_samples     = nb_samples;
 +    outbuf->audio->channel_layout = outlink->channel_layout;
 +
 +    while (nb_samples) {
 +        ns = nb_samples;
 +        for (i = 0; i < am->nb_inputs; i++)
 +            ns = FFMIN(ns, inbuf[i]->audio->nb_samples - am->in[i].pos);
 +        /* Unroll the most common sample formats: speed +~350% for the loop,
 +           +~13% overall (including two common decoders) */
 +        switch (am->bps) {
 +            case 1:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 1);
 +                break;
 +            case 2:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 2);
 +                break;
 +            case 4:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 4);
 +                break;
 +            default:
 +                copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, am->bps);
 +                break;
 +        }
 +
 +        nb_samples -= ns;
 +        for (i = 0; i < am->nb_inputs; i++) {
 +            am->in[i].nb_samples -= ns;
 +            am->in[i].pos += ns;
 +            if (am->in[i].pos == inbuf[i]->audio->nb_samples) {
 +                am->in[i].pos = 0;
 +                avfilter_unref_buffer(inbuf[i]);
 +                ff_bufqueue_get(&am->in[i].queue);
 +                inbuf[i] = ff_bufqueue_peek(&am->in[i].queue, 0);
 +                ins[i] = inbuf[i] ? inbuf[i]->data[0] : NULL;
 +            }
 +        }
 +    }
 +    ff_filter_samples(ctx->outputs[0], outbuf);
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    AMergeContext *am = ctx->priv;
 +    int ret, i;
 +    char name[16];
 +
 +    am->class = &amerge_class;
 +    av_opt_set_defaults(am);
 +    ret = av_set_options_string(am, args, "=", ":");
 +    if (ret < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error parsing options: '%s'\n", args);
 +        return ret;
 +    }
 +    am->in = av_calloc(am->nb_inputs, sizeof(*am->in));
 +    if (!am->in)
 +        return AVERROR(ENOMEM);
 +    for (i = 0; i < am->nb_inputs; i++) {
 +        AVFilterPad pad = {
 +            .name             = name,
 +            .type             = AVMEDIA_TYPE_AUDIO,
 +            .filter_samples   = filter_samples,
 +            .min_perms        = AV_PERM_READ | AV_PERM_PRESERVE,
 +        };
 +        snprintf(name, sizeof(name), "in%d", i);
 +        ff_insert_inpad(ctx, i, &pad);
 +    }
 +    return 0;
 +}
 +
 +AVFilter avfilter_af_amerge = {
 +    .name          = "amerge",
 +    .description   = NULL_IF_CONFIG_SMALL("Merge two audio streams into "
 +                                          "a single multi-channel stream."),
 +    .priv_size     = sizeof(AMergeContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) { { .name = NULL } },
 +    .outputs   = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .config_props     = config_output,
 +          .request_frame    = request_frame, },
 +        { .name = NULL }
 +    },
 +};
Simple merge
index 170b5bc,0000000..095a2b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,266 -1,0 +1,266 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Stefano Sabatini
 + * Copyright (c) 2011 Mina Nagy Zaki
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * resampling audio filter
 + */
 +
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "libavutil/avassert.h"
 +#include "libswresample/swresample.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    double ratio;
 +    struct SwrContext *swr;
 +    int64_t next_pts;
 +    int req_fullfilled;
 +} AResampleContext;
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    AResampleContext *aresample = ctx->priv;
 +    int ret = 0;
 +    char *argd = av_strdup(args);
 +
 +    aresample->next_pts = AV_NOPTS_VALUE;
 +    aresample->swr = swr_alloc();
 +    if (!aresample->swr)
 +        return AVERROR(ENOMEM);
 +
 +    if (args) {
 +        char *ptr=argd, *token;
 +
 +        while(token = av_strtok(ptr, ":", &ptr)) {
 +            char *value;
 +            av_strtok(token, "=", &value);
 +
 +            if(value) {
 +                if((ret=av_opt_set(aresample->swr, token, value, 0)) < 0)
 +                    goto end;
 +            } else {
 +                int out_rate;
 +                if ((ret = ff_parse_sample_rate(&out_rate, token, ctx)) < 0)
 +                    goto end;
 +                if((ret = av_opt_set_int(aresample->swr, "osr", out_rate, 0)) < 0)
 +                    goto end;
 +            }
 +        }
 +    }
 +end:
 +    av_free(argd);
 +    return ret;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AResampleContext *aresample = ctx->priv;
 +    swr_free(&aresample->swr);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AResampleContext *aresample = ctx->priv;
 +    int out_rate                   = av_get_int(aresample->swr, "osr", NULL);
 +    uint64_t out_layout            = av_get_int(aresample->swr, "ocl", NULL);
 +    enum AVSampleFormat out_format = av_get_int(aresample->swr, "osf", NULL);
 +
 +    AVFilterLink *inlink  = ctx->inputs[0];
 +    AVFilterLink *outlink = ctx->outputs[0];
 +
 +    AVFilterFormats        *in_formats      = ff_all_formats(AVMEDIA_TYPE_AUDIO);
 +    AVFilterFormats        *out_formats;
 +    AVFilterFormats        *in_samplerates  = ff_all_samplerates();
 +    AVFilterFormats        *out_samplerates;
 +    AVFilterChannelLayouts *in_layouts      = ff_all_channel_layouts();
 +    AVFilterChannelLayouts *out_layouts;
 +
 +    ff_formats_ref  (in_formats,      &inlink->out_formats);
 +    ff_formats_ref  (in_samplerates,  &inlink->out_samplerates);
 +    ff_channel_layouts_ref(in_layouts,      &inlink->out_channel_layouts);
 +
 +    if(out_rate > 0) {
 +        out_samplerates = ff_make_format_list((int[]){ out_rate, -1 });
 +    } else {
 +        out_samplerates = ff_all_samplerates();
 +    }
 +    ff_formats_ref(out_samplerates, &outlink->in_samplerates);
 +
 +    if(out_format != AV_SAMPLE_FMT_NONE) {
 +        out_formats = ff_make_format_list((int[]){ out_format, -1 });
 +    } else
 +        out_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO);
 +    ff_formats_ref(out_formats, &outlink->in_formats);
 +
 +    if(out_layout) {
 +        out_layouts = avfilter_make_format64_list((int64_t[]){ out_layout, -1 });
 +    } else
 +        out_layouts = ff_all_channel_layouts();
 +    ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts);
 +
 +    return 0;
 +}
 +
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    int ret;
 +    AVFilterContext *ctx = outlink->src;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    AResampleContext *aresample = ctx->priv;
 +    int out_rate;
 +    uint64_t out_layout;
 +    enum AVSampleFormat out_format;
 +    char inchl_buf[128], outchl_buf[128];
 +
 +    aresample->swr = swr_alloc_set_opts(aresample->swr,
 +                                        outlink->channel_layout, outlink->format, outlink->sample_rate,
 +                                        inlink->channel_layout, inlink->format, inlink->sample_rate,
 +                                        0, ctx);
 +    if (!aresample->swr)
 +        return AVERROR(ENOMEM);
 +
 +    ret = swr_init(aresample->swr);
 +    if (ret < 0)
 +        return ret;
 +
 +    out_rate   = av_get_int(aresample->swr, "osr", NULL);
 +    out_layout = av_get_int(aresample->swr, "ocl", NULL);
 +    out_format = av_get_int(aresample->swr, "osf", NULL);
 +    outlink->time_base = (AVRational) {1, out_rate};
 +
 +    av_assert0(outlink->sample_rate == out_rate);
 +    av_assert0(outlink->channel_layout == out_layout);
 +    av_assert0(outlink->format == out_format);
 +
 +    aresample->ratio = (double)outlink->sample_rate / inlink->sample_rate;
 +
 +    av_get_channel_layout_string(inchl_buf,  sizeof(inchl_buf),  -1, inlink ->channel_layout);
 +    av_get_channel_layout_string(outchl_buf, sizeof(outchl_buf), -1, outlink->channel_layout);
 +
 +    av_log(ctx, AV_LOG_INFO, "chl:%s fmt:%s r:%dHz -> chl:%s fmt:%s r:%dHz\n",
 +           inchl_buf,  av_get_sample_fmt_name(inlink->format),  inlink->sample_rate,
 +           outchl_buf, av_get_sample_fmt_name(outlink->format), outlink->sample_rate);
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamplesref)
 +{
 +    AResampleContext *aresample = inlink->dst->priv;
 +    const int n_in  = insamplesref->audio->nb_samples;
 +    int n_out       = n_in * aresample->ratio * 2 ;
 +    AVFilterLink *const outlink = inlink->dst->outputs[0];
 +    AVFilterBufferRef *outsamplesref = ff_get_audio_buffer(outlink, AV_PERM_WRITE, n_out);
 +
 +
 +    avfilter_copy_buffer_ref_props(outsamplesref, insamplesref);
 +
 +    if(insamplesref->pts != AV_NOPTS_VALUE) {
 +        int64_t inpts = av_rescale(insamplesref->pts, inlink->time_base.num * (int64_t)outlink->sample_rate * inlink->sample_rate, inlink->time_base.den);
 +        int64_t outpts= swr_next_pts(aresample->swr, inpts);
 +        aresample->next_pts =
 +        outsamplesref->pts  = (outpts + inlink->sample_rate/2) / inlink->sample_rate;
 +    } else {
 +        outsamplesref->pts  = AV_NOPTS_VALUE;
 +    }
 +
 +    n_out = swr_convert(aresample->swr, outsamplesref->extended_data, n_out,
 +                                 (void *)insamplesref->extended_data, n_in);
 +    if (n_out <= 0) {
 +        avfilter_unref_buffer(outsamplesref);
 +        avfilter_unref_buffer(insamplesref);
 +        return;
 +    }
 +
 +    outsamplesref->audio->sample_rate = outlink->sample_rate;
 +    outsamplesref->audio->nb_samples  = n_out;
 +
 +    ff_filter_samples(outlink, outsamplesref);
 +    aresample->req_fullfilled= 1;
 +    avfilter_unref_buffer(insamplesref);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AResampleContext *aresample = ctx->priv;
 +    AVFilterLink *const inlink = outlink->src->inputs[0];
 +    int ret;
 +
 +    aresample->req_fullfilled = 0;
 +    do{
 +        ret = ff_request_frame(ctx->inputs[0]);
 +    }while(!aresample->req_fullfilled && ret>=0);
 +
 +    if (ret == AVERROR_EOF) {
 +        AVFilterBufferRef *outsamplesref;
 +        int n_out = 4096;
 +
 +        outsamplesref = ff_get_audio_buffer(outlink, AV_PERM_WRITE, n_out);
 +        if (!outsamplesref)
 +            return AVERROR(ENOMEM);
 +        n_out = swr_convert(aresample->swr, outsamplesref->extended_data, n_out, 0, 0);
 +        if (n_out <= 0) {
 +            avfilter_unref_buffer(outsamplesref);
 +            return (n_out == 0) ? AVERROR_EOF : n_out;
 +        }
 +
 +        outsamplesref->audio->sample_rate = outlink->sample_rate;
 +        outsamplesref->audio->nb_samples  = n_out;
 +#if 0
 +        outsamplesref->pts = aresample->next_pts;
 +        if(aresample->next_pts != AV_NOPTS_VALUE)
 +            aresample->next_pts += av_rescale_q(n_out, (AVRational){1 ,outlink->sample_rate}, outlink->time_base);
 +#else
 +        outsamplesref->pts = (swr_next_pts(aresample->swr, INT64_MIN) + inlink->sample_rate/2) / inlink->sample_rate;
 +#endif
 +
 +        ff_filter_samples(outlink, outsamplesref);
 +        return 0;
 +    }
 +    return ret;
 +}
 +
 +AVFilter avfilter_af_aresample = {
 +    .name          = "aresample",
 +    .description   = NULL_IF_CONFIG_SMALL("Resample audio data."),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +    .priv_size     = sizeof(AResampleContext),
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .type            = AVMEDIA_TYPE_AUDIO,
 +                                    .filter_samples  = filter_samples,
 +                                    .min_perms       = AV_PERM_READ, },
 +                                  { .name = NULL}},
 +    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .config_props    = config_output,
 +                                    .request_frame   = request_frame,
 +                                    .type            = AVMEDIA_TYPE_AUDIO, },
 +                                  { .name = NULL}},
 +};
index 9ce4374,0000000..7a6c381
mode 100644,000000..100644
--- /dev/null
@@@ -1,204 -1,0 +1,204 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2012 Andrey Utkin
 + * Copyright (c) 2012 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Filter that changes number of samples on single output operation
 + */
 +
 +#include "libavutil/audio_fifo.h"
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/opt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +#include "formats.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    int nb_out_samples;  ///< how many samples to output
 +    AVAudioFifo *fifo;   ///< samples are queued here
 +    int64_t next_out_pts;
 +    int req_fullfilled;
 +    int pad;
 +} ASNSContext;
 +
 +#define OFFSET(x) offsetof(ASNSContext, x)
 +
 +static const AVOption asetnsamples_options[] = {
 +{ "pad", "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
 +{ "p",   "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
 +{ "nb_out_samples", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.dbl=1024}, 1, INT_MAX },
 +{ "n",              "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.dbl=1024}, 1, INT_MAX },
 +{ NULL }
 +};
 +
 +AVFILTER_DEFINE_CLASS(asetnsamples);
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    ASNSContext *asns = ctx->priv;
 +    int err;
 +
 +    asns->class = &asetnsamples_class;
 +    av_opt_set_defaults(asns);
 +
 +    if ((err = av_set_options_string(asns, args, "=", ":")) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
 +        return err;
 +    }
 +
 +    asns->next_out_pts = AV_NOPTS_VALUE;
 +    av_log(ctx, AV_LOG_INFO, "nb_out_samples:%d pad:%d\n", asns->nb_out_samples, asns->pad);
 +
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    ASNSContext *asns = ctx->priv;
 +    av_audio_fifo_free(asns->fifo);
 +}
 +
 +static int config_props_output(AVFilterLink *outlink)
 +{
 +    ASNSContext *asns = outlink->src->priv;
 +    int nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout);
 +
 +    asns->fifo = av_audio_fifo_alloc(outlink->format, nb_channels, asns->nb_out_samples);
 +    if (!asns->fifo)
 +        return AVERROR(ENOMEM);
 +
 +    return 0;
 +}
 +
 +static int push_samples(AVFilterLink *outlink)
 +{
 +    ASNSContext *asns = outlink->src->priv;
 +    AVFilterBufferRef *outsamples = NULL;
 +    int nb_out_samples, nb_pad_samples;
 +
 +    if (asns->pad) {
 +        nb_out_samples = av_audio_fifo_size(asns->fifo) ? asns->nb_out_samples : 0;
 +        nb_pad_samples = nb_out_samples - FFMIN(nb_out_samples, av_audio_fifo_size(asns->fifo));
 +    } else {
 +        nb_out_samples = FFMIN(asns->nb_out_samples, av_audio_fifo_size(asns->fifo));
 +        nb_pad_samples = 0;
 +    }
 +
 +    if (!nb_out_samples)
 +        return 0;
 +
 +    outsamples = ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_out_samples);
 +    av_assert0(outsamples);
 +
 +    av_audio_fifo_read(asns->fifo,
 +                       (void **)outsamples->extended_data, nb_out_samples);
 +
 +    if (nb_pad_samples)
 +        av_samples_set_silence(outsamples->extended_data, nb_out_samples - nb_pad_samples,
 +                               nb_pad_samples, av_get_channel_layout_nb_channels(outlink->channel_layout),
 +                               outlink->format);
 +    outsamples->audio->nb_samples     = nb_out_samples;
 +    outsamples->audio->channel_layout = outlink->channel_layout;
 +    outsamples->audio->sample_rate    = outlink->sample_rate;
 +    outsamples->pts = asns->next_out_pts;
 +
 +    if (asns->next_out_pts != AV_NOPTS_VALUE)
 +        asns->next_out_pts += nb_out_samples;
 +
 +    ff_filter_samples(outlink, outsamples);
 +    asns->req_fullfilled = 1;
 +    return nb_out_samples;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    ASNSContext *asns = ctx->priv;
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    int ret;
 +    int nb_samples = insamples->audio->nb_samples;
 +
 +    if (av_audio_fifo_space(asns->fifo) < nb_samples) {
 +        av_log(ctx, AV_LOG_DEBUG, "No space for %d samples, stretching audio fifo\n", nb_samples);
 +        ret = av_audio_fifo_realloc(asns->fifo, av_audio_fifo_size(asns->fifo) + nb_samples);
 +        if (ret < 0) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Stretching audio fifo failed, discarded %d samples\n", nb_samples);
 +            return;
 +        }
 +    }
 +    av_audio_fifo_write(asns->fifo, (void **)insamples->extended_data, nb_samples);
 +    if (asns->next_out_pts == AV_NOPTS_VALUE)
 +        asns->next_out_pts = insamples->pts;
 +    avfilter_unref_buffer(insamples);
 +
 +    if (av_audio_fifo_size(asns->fifo) >= asns->nb_out_samples)
 +        push_samples(outlink);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    ASNSContext *asns = outlink->src->priv;
 +    AVFilterLink *inlink = outlink->src->inputs[0];
 +    int ret;
 +
 +    asns->req_fullfilled = 0;
 +    do {
 +        ret = ff_request_frame(inlink);
 +    } while (!asns->req_fullfilled && ret >= 0);
 +
 +    if (ret == AVERROR_EOF)
 +        while (push_samples(outlink))
 +            ;
 +
 +    return ret;
 +}
 +
 +AVFilter avfilter_af_asetnsamples = {
 +    .name           = "asetnsamples",
 +    .description    = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."),
 +    .priv_size      = sizeof(ASNSContext),
 +    .init           = init,
 +    .uninit         = uninit,
 +
 +    .inputs  = (const AVFilterPad[]) {
 +        {
 +            .name           = "default",
 +            .type           = AVMEDIA_TYPE_AUDIO,
 +            .filter_samples = filter_samples,
 +            .min_perms      = AV_PERM_READ|AV_PERM_WRITE
 +        },
 +        { .name = NULL }
 +    },
 +
 +    .outputs = (const AVFilterPad[]) {
 +        {
 +            .name           = "default",
 +            .type           = AVMEDIA_TYPE_AUDIO,
 +            .request_frame  = request_frame,
 +            .config_props   = config_props_output,
 +        },
 +        { .name = NULL }
 +    },
 +};
index d4198e9,0000000..d774ec7
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,106 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * filter for showing textual audio frame information
 + */
 +
 +#include "libavutil/adler32.h"
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/timestamp.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +
 +typedef struct {
 +    unsigned int frame;
 +} ShowInfoContext;
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    ShowInfoContext *showinfo = ctx->priv;
 +    showinfo->frame = 0;
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *samplesref)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    ShowInfoContext *showinfo = ctx->priv;
 +    uint32_t plane_checksum[8] = {0}, checksum = 0;
 +    char chlayout_str[128];
 +    int plane;
 +    int linesize =
 +        samplesref->audio->nb_samples *
 +        av_get_bytes_per_sample(samplesref->format);
 +    if (!av_sample_fmt_is_planar(samplesref->format))
 +        linesize *= av_get_channel_layout_nb_channels(samplesref->audio->channel_layout);
 +
 +    for (plane = 0; samplesref->data[plane] && plane < 8; plane++) {
 +        uint8_t *data = samplesref->data[plane];
 +
 +        plane_checksum[plane] = av_adler32_update(plane_checksum[plane],
 +                                                  data, linesize);
 +        checksum = av_adler32_update(checksum, data, linesize);
 +    }
 +
 +    av_get_channel_layout_string(chlayout_str, sizeof(chlayout_str), -1,
 +                                 samplesref->audio->channel_layout);
 +
 +    av_log(ctx, AV_LOG_INFO,
 +           "n:%d pts:%s pts_time:%s pos:%"PRId64" "
 +           "fmt:%s chlayout:%s nb_samples:%d rate:%d "
 +           "checksum:%08X plane_checksum[%08X",
 +           showinfo->frame,
 +           av_ts2str(samplesref->pts), av_ts2timestr(samplesref->pts, &inlink->time_base),
 +           samplesref->pos,
 +           av_get_sample_fmt_name(samplesref->format),
 +           chlayout_str,
 +           samplesref->audio->nb_samples,
 +           samplesref->audio->sample_rate,
 +           checksum,
 +           plane_checksum[0]);
 +
 +    for (plane = 1; samplesref->data[plane] && plane < 8; plane++)
 +        av_log(ctx, AV_LOG_INFO, " %08X", plane_checksum[plane]);
 +    av_log(ctx, AV_LOG_INFO, "]\n");
 +
 +    showinfo->frame++;
 +    ff_filter_samples(inlink->dst->outputs[0], samplesref);
 +}
 +
 +AVFilter avfilter_af_ashowinfo = {
 +    .name        = "ashowinfo",
 +    .description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."),
 +
 +    .priv_size = sizeof(ShowInfoContext),
 +    .init      = init,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name       = "default",
 +                                    .type             = AVMEDIA_TYPE_AUDIO,
 +                                    .get_audio_buffer = ff_null_get_audio_buffer,
 +                                    .filter_samples   = filter_samples,
 +                                    .min_perms        = AV_PERM_READ, },
 +                                  { .name = NULL}},
 +
 +    .outputs   = (const AVFilterPad[]) {{ .name       = "default",
 +                                    .type             = AVMEDIA_TYPE_AUDIO },
 +                                  { .name = NULL}},
 +};
index ff3f3c2,0000000..8cf3f39
mode 100644,000000..100644
--- /dev/null
@@@ -1,210 -1,0 +1,210 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
 +/*
 + * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Stream (de)synchronization filter
 + */
 +
 +#include "libavutil/eval.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +#define QUEUE_SIZE 16
 +
 +static const char * const var_names[] = {
 +    "b1", "b2",
 +    "s1", "s2",
 +    "t1", "t2",
 +    NULL
 +};
 +
 +enum var_name {
 +    VAR_B1, VAR_B2,
 +    VAR_S1, VAR_S2,
 +    VAR_T1, VAR_T2,
 +    VAR_NB
 +};
 +
 +typedef struct {
 +    AVExpr *expr;
 +    double var_values[VAR_NB];
 +    struct buf_queue {
 +        AVFilterBufferRef *buf[QUEUE_SIZE];
 +        unsigned tail, nb;
 +        /* buf[tail] is the oldest,
 +           buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
 +    } queue[2];
 +    int req[2];
 +    int next_out;
 +    int eof; /* bitmask, one bit for each stream */
 +} AStreamSyncContext;
 +
 +static const char *default_expr = "t1-t2";
 +
++static av_cold int init(AVFilterContext *ctx, const char *args0)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +    const char *expr = args0 ? args0 : default_expr;
 +    int r, i;
 +
 +    r = av_expr_parse(&as->expr, expr, var_names,
 +                      NULL, NULL, NULL, NULL, 0, ctx);
 +    if (r < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", expr);
 +        return r;
 +    }
 +    for (i = 0; i < 42; i++)
 +        av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    int i;
 +    AVFilterFormats *formats, *rates;
 +    AVFilterChannelLayouts *layouts;
 +
 +    for (i = 0; i < 2; i++) {
 +        formats = ctx->inputs[i]->in_formats;
 +        ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
 +        ff_formats_ref(formats, &ctx->outputs[i]->in_formats);
 +        rates = ff_all_samplerates();
 +        ff_formats_ref(rates, &ctx->inputs[i]->out_samplerates);
 +        ff_formats_ref(rates, &ctx->outputs[i]->in_samplerates);
 +        layouts = ctx->inputs[i]->in_channel_layouts;
 +        ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts);
 +        ff_channel_layouts_ref(layouts, &ctx->outputs[i]->in_channel_layouts);
 +    }
 +    return 0;
 +}
 +
 +static int config_output(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    int id = outlink == ctx->outputs[1];
 +
 +    outlink->sample_rate = ctx->inputs[id]->sample_rate;
 +    outlink->time_base   = ctx->inputs[id]->time_base;
 +    return 0;
 +}
 +
 +static void send_out(AVFilterContext *ctx, int out_id)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +    struct buf_queue *queue = &as->queue[out_id];
 +    AVFilterBufferRef *buf = queue->buf[queue->tail];
 +
 +    queue->buf[queue->tail] = NULL;
 +    as->var_values[VAR_B1 + out_id]++;
 +    as->var_values[VAR_S1 + out_id] += buf->audio->nb_samples;
 +    if (buf->pts != AV_NOPTS_VALUE)
 +        as->var_values[VAR_T1 + out_id] =
 +            av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
 +    as->var_values[VAR_T1 + out_id] += buf->audio->nb_samples /
 +                                   (double)ctx->inputs[out_id]->sample_rate;
 +    ff_filter_samples(ctx->outputs[out_id], buf);
 +    queue->nb--;
 +    queue->tail = (queue->tail + 1) % QUEUE_SIZE;
 +    if (as->req[out_id])
 +        as->req[out_id]--;
 +}
 +
 +static void send_next(AVFilterContext *ctx)
 +{
 +    AStreamSyncContext *as = ctx->priv;
 +    int i;
 +
 +    while (1) {
 +        if (!as->queue[as->next_out].nb)
 +            break;
 +        send_out(ctx, as->next_out);
 +        if (!as->eof)
 +            as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
 +    }
 +    for (i = 0; i < 2; i++)
 +        if (as->queue[i].nb == QUEUE_SIZE)
 +            send_out(ctx, i);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    AStreamSyncContext *as = ctx->priv;
 +    int id = outlink == ctx->outputs[1];
 +
 +    as->req[id]++;
 +    while (as->req[id] && !(as->eof & (1 << id))) {
 +        if (as->queue[as->next_out].nb) {
 +            send_next(ctx);
 +        } else {
 +            as->eof |= 1 << as->next_out;
 +            ff_request_frame(ctx->inputs[as->next_out]);
 +            if (as->eof & (1 << as->next_out))
 +                as->next_out = !as->next_out;
 +        }
 +    }
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AStreamSyncContext *as = ctx->priv;
 +    int id = inlink == ctx->inputs[1];
 +
 +    as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
 +        insamples;
 +    as->eof &= ~(1 << id);
 +    send_next(ctx);
 +}
 +
 +AVFilter avfilter_af_astreamsync = {
 +    .name          = "astreamsync",
 +    .description   = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
 +                                          "in a configurable order."),
 +    .priv_size     = sizeof(AStreamSyncContext),
 +    .init          = init,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {
 +        { .name             = "in1",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .filter_samples   = filter_samples,
 +          .min_perms        = AV_PERM_READ, },
 +        { .name             = "in2",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .filter_samples   = filter_samples,
 +          .min_perms        = AV_PERM_READ, },
 +        { .name = NULL }
 +    },
 +    .outputs   = (const AVFilterPad[]) {
 +        { .name             = "out1",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .config_props     = config_output,
 +          .request_frame    = request_frame, },
 +        { .name             = "out2",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .config_props     = config_output,
 +          .request_frame    = request_frame, },
 +        { .name = NULL }
 +    },
 +};
@@@ -49,9 -49,14 +49,9 @@@ static const AVOption asyncts_options[
      { NULL },
  };
  
 -static const AVClass async_class = {
 -    .class_name = "asyncts filter",
 -    .item_name  = av_default_item_name,
 -    .option     = options,
 -    .version    = LIBAVUTIL_VERSION_INT,
 -};
 +AVFILTER_DEFINE_CLASS(asyncts);
  
- static int init(AVFilterContext *ctx, const char *args, void *opaque)
+ static int init(AVFilterContext *ctx, const char *args)
  {
      ASyncContext *s = ctx->priv;
      int ret;
index 15d06d6,0000000..7a08503
mode 100644,000000..100644
--- /dev/null
@@@ -1,1161 -1,0 +1,1161 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2012 Pavel Koshevoy <pkoshevoy at gmail dot com>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * tempo scaling audio filter -- an implementation of WSOLA algorithm
 + *
 + * Based on MIT licensed yaeAudioTempoFilter.h and yaeAudioFragment.h
 + * from Apprentice Video player by Pavel Koshevoy.
 + * https://sourceforge.net/projects/apprenticevideo/
 + *
 + * An explanation of SOLA algorithm is available at
 + * http://www.surina.net/article/time-and-pitch-scaling.html
 + *
 + * WSOLA is very similar to SOLA, only one major difference exists between
 + * these algorithms.  SOLA shifts audio fragments along the output stream,
 + * where as WSOLA shifts audio fragments along the input stream.
 + *
 + * The advantage of WSOLA algorithm is that the overlap region size is
 + * always the same, therefore the blending function is constant and
 + * can be precomputed.
 + */
 +
 +#include <float.h>
 +#include "libavcodec/avfft.h"
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/eval.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/samplefmt.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +/**
 + * A fragment of audio waveform
 + */
 +typedef struct {
 +    // index of the first sample of this fragment in the overall waveform;
 +    // 0: input sample position
 +    // 1: output sample position
 +    int64_t position[2];
 +
 +    // original packed multi-channel samples:
 +    uint8_t *data;
 +
 +    // number of samples in this fragment:
 +    int nsamples;
 +
 +    // rDFT transform of the down-mixed mono fragment, used for
 +    // fast waveform alignment via correlation in frequency domain:
 +    FFTSample *xdat;
 +} AudioFragment;
 +
 +/**
 + * Filter state machine states
 + */
 +typedef enum {
 +    YAE_LOAD_FRAGMENT,
 +    YAE_ADJUST_POSITION,
 +    YAE_RELOAD_FRAGMENT,
 +    YAE_OUTPUT_OVERLAP_ADD,
 +    YAE_FLUSH_OUTPUT,
 +} FilterState;
 +
 +/**
 + * Filter state machine
 + */
 +typedef struct {
 +    // ring-buffer of input samples, necessary because some times
 +    // input fragment position may be adjusted backwards:
 +    uint8_t *buffer;
 +
 +    // ring-buffer maximum capacity, expressed in sample rate time base:
 +    int ring;
 +
 +    // ring-buffer house keeping:
 +    int size;
 +    int head;
 +    int tail;
 +
 +    // 0: input sample position corresponding to the ring buffer tail
 +    // 1: output sample position
 +    int64_t position[2];
 +
 +    // sample format:
 +    enum AVSampleFormat format;
 +
 +    // number of channels:
 +    int channels;
 +
 +    // row of bytes to skip from one sample to next, across multple channels;
 +    // stride = (number-of-channels * bits-per-sample-per-channel) / 8
 +    int stride;
 +
 +    // fragment window size, power-of-two integer:
 +    int window;
 +
 +    // Hann window coefficients, for feathering
 +    // (blending) the overlapping fragment region:
 +    float *hann;
 +
 +    // tempo scaling factor:
 +    double tempo;
 +
 +    // cumulative alignment drift:
 +    int drift;
 +
 +    // current/previous fragment ring-buffer:
 +    AudioFragment frag[2];
 +
 +    // current fragment index:
 +    uint64_t nfrag;
 +
 +    // current state:
 +    FilterState state;
 +
 +    // for fast correlation calculation in frequency domain:
 +    RDFTContext *real_to_complex;
 +    RDFTContext *complex_to_real;
 +    FFTSample *correlation;
 +
 +    // for managing AVFilterPad.request_frame and AVFilterPad.filter_samples
 +    int request_fulfilled;
 +    AVFilterBufferRef *dst_buffer;
 +    uint8_t *dst;
 +    uint8_t *dst_end;
 +    uint64_t nsamples_in;
 +    uint64_t nsamples_out;
 +} ATempoContext;
 +
 +/**
 + * Reset filter to initial state, do not deallocate existing local buffers.
 + */
 +static void yae_clear(ATempoContext *atempo)
 +{
 +    atempo->size = 0;
 +    atempo->head = 0;
 +    atempo->tail = 0;
 +
 +    atempo->drift = 0;
 +    atempo->nfrag = 0;
 +    atempo->state = YAE_LOAD_FRAGMENT;
 +
 +    atempo->position[0] = 0;
 +    atempo->position[1] = 0;
 +
 +    atempo->frag[0].position[0] = 0;
 +    atempo->frag[0].position[1] = 0;
 +    atempo->frag[0].nsamples    = 0;
 +
 +    atempo->frag[1].position[0] = 0;
 +    atempo->frag[1].position[1] = 0;
 +    atempo->frag[1].nsamples    = 0;
 +
 +    // shift left position of 1st fragment by half a window
 +    // so that no re-normalization would be required for
 +    // the left half of the 1st fragment:
 +    atempo->frag[0].position[0] = -(int64_t)(atempo->window / 2);
 +    atempo->frag[0].position[1] = -(int64_t)(atempo->window / 2);
 +
 +    avfilter_unref_bufferp(&atempo->dst_buffer);
 +    atempo->dst     = NULL;
 +    atempo->dst_end = NULL;
 +
 +    atempo->request_fulfilled = 0;
 +    atempo->nsamples_in       = 0;
 +    atempo->nsamples_out      = 0;
 +}
 +
 +/**
 + * Reset filter to initial state and deallocate all buffers.
 + */
 +static void yae_release_buffers(ATempoContext *atempo)
 +{
 +    yae_clear(atempo);
 +
 +    av_freep(&atempo->frag[0].data);
 +    av_freep(&atempo->frag[1].data);
 +    av_freep(&atempo->frag[0].xdat);
 +    av_freep(&atempo->frag[1].xdat);
 +
 +    av_freep(&atempo->buffer);
 +    av_freep(&atempo->hann);
 +    av_freep(&atempo->correlation);
 +
 +    av_rdft_end(atempo->real_to_complex);
 +    atempo->real_to_complex = NULL;
 +
 +    av_rdft_end(atempo->complex_to_real);
 +    atempo->complex_to_real = NULL;
 +}
 +
 +#define REALLOC_OR_FAIL(field, field_size)                      \
 +    do {                                                        \
 +        void * new_field = av_realloc(field, (field_size));     \
 +        if (!new_field) {                                       \
 +            yae_release_buffers(atempo);                        \
 +            return AVERROR(ENOMEM);                             \
 +        }                                                       \
 +        field = new_field;                                      \
 +    } while (0)
 +
 +/**
 + * Prepare filter for processing audio data of given format,
 + * sample rate and number of channels.
 + */
 +static int yae_reset(ATempoContext *atempo,
 +                     enum AVSampleFormat format,
 +                     int sample_rate,
 +                     int channels)
 +{
 +    const int sample_size = av_get_bytes_per_sample(format);
 +    uint32_t nlevels  = 0;
 +    uint32_t pot;
 +    int i;
 +
 +    atempo->format   = format;
 +    atempo->channels = channels;
 +    atempo->stride   = sample_size * channels;
 +
 +    // pick a segment window size:
 +    atempo->window = sample_rate / 24;
 +
 +    // adjust window size to be a power-of-two integer:
 +    nlevels = av_log2(atempo->window);
 +    pot = 1 << nlevels;
 +    av_assert0(pot <= atempo->window);
 +
 +    if (pot < atempo->window) {
 +        atempo->window = pot * 2;
 +        nlevels++;
 +    }
 +
 +    // initialize audio fragment buffers:
 +    REALLOC_OR_FAIL(atempo->frag[0].data, atempo->window * atempo->stride);
 +    REALLOC_OR_FAIL(atempo->frag[1].data, atempo->window * atempo->stride);
 +    REALLOC_OR_FAIL(atempo->frag[0].xdat, atempo->window * sizeof(FFTComplex));
 +    REALLOC_OR_FAIL(atempo->frag[1].xdat, atempo->window * sizeof(FFTComplex));
 +
 +    // initialize rDFT contexts:
 +    av_rdft_end(atempo->real_to_complex);
 +    atempo->real_to_complex = NULL;
 +
 +    av_rdft_end(atempo->complex_to_real);
 +    atempo->complex_to_real = NULL;
 +
 +    atempo->real_to_complex = av_rdft_init(nlevels + 1, DFT_R2C);
 +    if (!atempo->real_to_complex) {
 +        yae_release_buffers(atempo);
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    atempo->complex_to_real = av_rdft_init(nlevels + 1, IDFT_C2R);
 +    if (!atempo->complex_to_real) {
 +        yae_release_buffers(atempo);
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    REALLOC_OR_FAIL(atempo->correlation, atempo->window * sizeof(FFTComplex));
 +
 +    atempo->ring = atempo->window * 3;
 +    REALLOC_OR_FAIL(atempo->buffer, atempo->ring * atempo->stride);
 +
 +    // initialize the Hann window function:
 +    REALLOC_OR_FAIL(atempo->hann, atempo->window * sizeof(float));
 +
 +    for (i = 0; i < atempo->window; i++) {
 +        double t = (double)i / (double)(atempo->window - 1);
 +        double h = 0.5 * (1.0 - cos(2.0 * M_PI * t));
 +        atempo->hann[i] = (float)h;
 +    }
 +
 +    yae_clear(atempo);
 +    return 0;
 +}
 +
 +static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
 +{
 +    ATempoContext *atempo = ctx->priv;
 +    char   *tail = NULL;
 +    double tempo = av_strtod(arg_tempo, &tail);
 +
 +    if (tail && *tail) {
 +        av_log(ctx, AV_LOG_ERROR, "Invalid tempo value '%s'\n", arg_tempo);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (tempo < 0.5 || tempo > 2.0) {
 +        av_log(ctx, AV_LOG_ERROR, "Tempo value %f exceeds [0.5, 2.0] range\n",
 +               tempo);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    atempo->tempo = tempo;
 +    return 0;
 +}
 +
 +inline static AudioFragment *yae_curr_frag(ATempoContext *atempo)
 +{
 +    return &atempo->frag[atempo->nfrag % 2];
 +}
 +
 +inline static AudioFragment *yae_prev_frag(ATempoContext *atempo)
 +{
 +    return &atempo->frag[(atempo->nfrag + 1) % 2];
 +}
 +
 +/**
 + * A helper macro for initializing complex data buffer with scalar data
 + * of a given type.
 + */
 +#define yae_init_xdat(scalar_type, scalar_max)                          \
 +    do {                                                                \
 +        const uint8_t *src_end = src +                                  \
 +            frag->nsamples * atempo->channels * sizeof(scalar_type);    \
 +                                                                        \
 +        FFTSample *xdat = frag->xdat;                                   \
 +        scalar_type tmp;                                                \
 +                                                                        \
 +        if (atempo->channels == 1) {                                    \
 +            for (; src < src_end; xdat++) {                             \
 +                tmp = *(const scalar_type *)src;                        \
 +                src += sizeof(scalar_type);                             \
 +                                                                        \
 +                *xdat = (FFTSample)tmp;                                 \
 +            }                                                           \
 +        } else {                                                        \
 +            FFTSample s, max, ti, si;                                   \
 +            int i;                                                      \
 +                                                                        \
 +            for (; src < src_end; xdat++) {                             \
 +                tmp = *(const scalar_type *)src;                        \
 +                src += sizeof(scalar_type);                             \
 +                                                                        \
 +                max = (FFTSample)tmp;                                   \
 +                s = FFMIN((FFTSample)scalar_max,                        \
 +                          (FFTSample)fabsf(max));                       \
 +                                                                        \
 +                for (i = 1; i < atempo->channels; i++) {                \
 +                    tmp = *(const scalar_type *)src;                    \
 +                    src += sizeof(scalar_type);                         \
 +                                                                        \
 +                    ti = (FFTSample)tmp;                                \
 +                    si = FFMIN((FFTSample)scalar_max,                   \
 +                               (FFTSample)fabsf(ti));                   \
 +                                                                        \
 +                    if (s < si) {                                       \
 +                        s   = si;                                       \
 +                        max = ti;                                       \
 +                    }                                                   \
 +                }                                                       \
 +                                                                        \
 +                *xdat = max;                                            \
 +            }                                                           \
 +        }                                                               \
 +    } while (0)
 +
 +/**
 + * Initialize complex data buffer of a given audio fragment
 + * with down-mixed mono data of appropriate scalar type.
 + */
 +static void yae_downmix(ATempoContext *atempo, AudioFragment *frag)
 +{
 +    // shortcuts:
 +    const uint8_t *src = frag->data;
 +
 +    // init complex data buffer used for FFT and Correlation:
 +    memset(frag->xdat, 0, sizeof(FFTComplex) * atempo->window);
 +
 +    if (atempo->format == AV_SAMPLE_FMT_U8) {
 +        yae_init_xdat(uint8_t, 127);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S16) {
 +        yae_init_xdat(int16_t, 32767);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S32) {
 +        yae_init_xdat(int, 2147483647);
 +    } else if (atempo->format == AV_SAMPLE_FMT_FLT) {
 +        yae_init_xdat(float, 1);
 +    } else if (atempo->format == AV_SAMPLE_FMT_DBL) {
 +        yae_init_xdat(double, 1);
 +    }
 +}
 +
 +/**
 + * Populate the internal data buffer on as-needed basis.
 + *
 + * @return
 + *   0 if requested data was already available or was successfully loaded,
 + *   AVERROR(EAGAIN) if more input data is required.
 + */
 +static int yae_load_data(ATempoContext *atempo,
 +                         const uint8_t **src_ref,
 +                         const uint8_t *src_end,
 +                         int64_t stop_here)
 +{
 +    // shortcut:
 +    const uint8_t *src = *src_ref;
 +    const int read_size = stop_here - atempo->position[0];
 +
 +    if (stop_here <= atempo->position[0]) {
 +        return 0;
 +    }
 +
 +    // samples are not expected to be skipped:
 +    av_assert0(read_size <= atempo->ring);
 +
 +    while (atempo->position[0] < stop_here && src < src_end) {
 +        int src_samples = (src_end - src) / atempo->stride;
 +
 +        // load data piece-wise, in order to avoid complicating the logic:
 +        int nsamples = FFMIN(read_size, src_samples);
 +        int na;
 +        int nb;
 +
 +        nsamples = FFMIN(nsamples, atempo->ring);
 +        na = FFMIN(nsamples, atempo->ring - atempo->tail);
 +        nb = FFMIN(nsamples - na, atempo->ring);
 +
 +        if (na) {
 +            uint8_t *a = atempo->buffer + atempo->tail * atempo->stride;
 +            memcpy(a, src, na * atempo->stride);
 +
 +            src += na * atempo->stride;
 +            atempo->position[0] += na;
 +
 +            atempo->size = FFMIN(atempo->size + na, atempo->ring);
 +            atempo->tail = (atempo->tail + na) % atempo->ring;
 +            atempo->head =
 +                atempo->size < atempo->ring ?
 +                atempo->tail - atempo->size :
 +                atempo->tail;
 +        }
 +
 +        if (nb) {
 +            uint8_t *b = atempo->buffer;
 +            memcpy(b, src, nb * atempo->stride);
 +
 +            src += nb * atempo->stride;
 +            atempo->position[0] += nb;
 +
 +            atempo->size = FFMIN(atempo->size + nb, atempo->ring);
 +            atempo->tail = (atempo->tail + nb) % atempo->ring;
 +            atempo->head =
 +                atempo->size < atempo->ring ?
 +                atempo->tail - atempo->size :
 +                atempo->tail;
 +        }
 +    }
 +
 +    // pass back the updated source buffer pointer:
 +    *src_ref = src;
 +
 +    // sanity check:
 +    av_assert0(atempo->position[0] <= stop_here);
 +
 +    return atempo->position[0] == stop_here ? 0 : AVERROR(EAGAIN);
 +}
 +
 +/**
 + * Populate current audio fragment data buffer.
 + *
 + * @return
 + *   0 when the fragment is ready,
 + *   AVERROR(EAGAIN) if more input data is required.
 + */
 +static int yae_load_frag(ATempoContext *atempo,
 +                         const uint8_t **src_ref,
 +                         const uint8_t *src_end)
 +{
 +    // shortcuts:
 +    AudioFragment *frag = yae_curr_frag(atempo);
 +    uint8_t *dst;
 +    int64_t missing, start, zeros;
 +    uint32_t nsamples;
 +    const uint8_t *a, *b;
 +    int i0, i1, n0, n1, na, nb;
 +
 +    int64_t stop_here = frag->position[0] + atempo->window;
 +    if (src_ref && yae_load_data(atempo, src_ref, src_end, stop_here) != 0) {
 +        return AVERROR(EAGAIN);
 +    }
 +
 +    // calculate the number of samples we don't have:
 +    missing =
 +        stop_here > atempo->position[0] ?
 +        stop_here - atempo->position[0] : 0;
 +
 +    nsamples =
 +        missing < (int64_t)atempo->window ?
 +        (uint32_t)(atempo->window - missing) : 0;
 +
 +    // setup the output buffer:
 +    frag->nsamples = nsamples;
 +    dst = frag->data;
 +
 +    start = atempo->position[0] - atempo->size;
 +    zeros = 0;
 +
 +    if (frag->position[0] < start) {
 +        // what we don't have we substitute with zeros:
 +        zeros = FFMIN(start - frag->position[0], (int64_t)nsamples);
 +        av_assert0(zeros != nsamples);
 +
 +        memset(dst, 0, zeros * atempo->stride);
 +        dst += zeros * atempo->stride;
 +    }
 +
 +    if (zeros == nsamples) {
 +        return 0;
 +    }
 +
 +    // get the remaining data from the ring buffer:
 +    na = (atempo->head < atempo->tail ?
 +          atempo->tail - atempo->head :
 +          atempo->ring - atempo->head);
 +
 +    nb = atempo->head < atempo->tail ? 0 : atempo->tail;
 +
 +    // sanity check:
 +    av_assert0(nsamples <= zeros + na + nb);
 +
 +    a = atempo->buffer + atempo->head * atempo->stride;
 +    b = atempo->buffer;
 +
 +    i0 = frag->position[0] + zeros - start;
 +    i1 = i0 < na ? 0 : i0 - na;
 +
 +    n0 = i0 < na ? FFMIN(na - i0, (int)(nsamples - zeros)) : 0;
 +    n1 = nsamples - zeros - n0;
 +
 +    if (n0) {
 +        memcpy(dst, a + i0 * atempo->stride, n0 * atempo->stride);
 +        dst += n0 * atempo->stride;
 +    }
 +
 +    if (n1) {
 +        memcpy(dst, b + i1 * atempo->stride, n1 * atempo->stride);
 +        dst += n1 * atempo->stride;
 +    }
 +
 +    return 0;
 +}
 +
 +/**
 + * Prepare for loading next audio fragment.
 + */
 +static void yae_advance_to_next_frag(ATempoContext *atempo)
 +{
 +    const double fragment_step = atempo->tempo * (double)(atempo->window / 2);
 +
 +    const AudioFragment *prev;
 +    AudioFragment       *frag;
 +
 +    atempo->nfrag++;
 +    prev = yae_prev_frag(atempo);
 +    frag = yae_curr_frag(atempo);
 +
 +    frag->position[0] = prev->position[0] + (int64_t)fragment_step;
 +    frag->position[1] = prev->position[1] + atempo->window / 2;
 +    frag->nsamples    = 0;
 +}
 +
 +/**
 + * Calculate cross-correlation via rDFT.
 + *
 + * Multiply two vectors of complex numbers (result of real_to_complex rDFT)
 + * and transform back via complex_to_real rDFT.
 + */
 +static void yae_xcorr_via_rdft(FFTSample *xcorr,
 +                               RDFTContext *complex_to_real,
 +                               const FFTComplex *xa,
 +                               const FFTComplex *xb,
 +                               const int window)
 +{
 +    FFTComplex *xc = (FFTComplex *)xcorr;
 +    int i;
 +
 +    // NOTE: first element requires special care -- Given Y = rDFT(X),
 +    // Im(Y[0]) and Im(Y[N/2]) are always zero, therefore av_rdft_calc
 +    // stores Re(Y[N/2]) in place of Im(Y[0]).
 +
 +    xc->re = xa->re * xb->re;
 +    xc->im = xa->im * xb->im;
 +    xa++;
 +    xb++;
 +    xc++;
 +
 +    for (i = 1; i < window; i++, xa++, xb++, xc++) {
 +        xc->re = (xa->re * xb->re + xa->im * xb->im);
 +        xc->im = (xa->im * xb->re - xa->re * xb->im);
 +    }
 +
 +    // apply inverse rDFT:
 +    av_rdft_calc(complex_to_real, xcorr);
 +}
 +
 +/**
 + * Calculate alignment offset for given fragment
 + * relative to the previous fragment.
 + *
 + * @return alignment offset of current fragment relative to previous.
 + */
 +static int yae_align(AudioFragment *frag,
 +                     const AudioFragment *prev,
 +                     const int window,
 +                     const int delta_max,
 +                     const int drift,
 +                     FFTSample *correlation,
 +                     RDFTContext *complex_to_real)
 +{
 +    int       best_offset = -drift;
 +    FFTSample best_metric = -FLT_MAX;
 +    FFTSample *xcorr;
 +
 +    int i0;
 +    int i1;
 +    int i;
 +
 +    yae_xcorr_via_rdft(correlation,
 +                       complex_to_real,
 +                       (const FFTComplex *)prev->xdat,
 +                       (const FFTComplex *)frag->xdat,
 +                       window);
 +
 +    // identify search window boundaries:
 +    i0 = FFMAX(window / 2 - delta_max - drift, 0);
 +    i0 = FFMIN(i0, window);
 +
 +    i1 = FFMIN(window / 2 + delta_max - drift, window - window / 16);
 +    i1 = FFMAX(i1, 0);
 +
 +    // identify cross-correlation peaks within search window:
 +    xcorr = correlation + i0;
 +
 +    for (i = i0; i < i1; i++, xcorr++) {
 +        FFTSample metric = *xcorr;
 +
 +        // normalize:
 +        FFTSample drifti = (FFTSample)(drift + i);
 +        metric *= drifti * (FFTSample)(i - i0) * (FFTSample)(i1 - i);
 +
 +        if (metric > best_metric) {
 +            best_metric = metric;
 +            best_offset = i - window / 2;
 +        }
 +    }
 +
 +    return best_offset;
 +}
 +
 +/**
 + * Adjust current fragment position for better alignment
 + * with previous fragment.
 + *
 + * @return alignment correction.
 + */
 +static int yae_adjust_position(ATempoContext *atempo)
 +{
 +    const AudioFragment *prev = yae_prev_frag(atempo);
 +    AudioFragment       *frag = yae_curr_frag(atempo);
 +
 +    const int delta_max  = atempo->window / 2;
 +    const int correction = yae_align(frag,
 +                                     prev,
 +                                     atempo->window,
 +                                     delta_max,
 +                                     atempo->drift,
 +                                     atempo->correlation,
 +                                     atempo->complex_to_real);
 +
 +    if (correction) {
 +        // adjust fragment position:
 +        frag->position[0] -= correction;
 +
 +        // clear so that the fragment can be reloaded:
 +        frag->nsamples = 0;
 +
 +        // update cumulative correction drift counter:
 +        atempo->drift += correction;
 +    }
 +
 +    return correction;
 +}
 +
 +/**
 + * A helper macro for blending the overlap region of previous
 + * and current audio fragment.
 + */
 +#define yae_blend(scalar_type)                                          \
 +    do {                                                                \
 +        const scalar_type *aaa = (const scalar_type *)a;                \
 +        const scalar_type *bbb = (const scalar_type *)b;                \
 +                                                                        \
 +        scalar_type *out     = (scalar_type *)dst;                      \
 +        scalar_type *out_end = (scalar_type *)dst_end;                  \
 +        int64_t i;                                                      \
 +                                                                        \
 +        for (i = 0; i < overlap && out < out_end;                       \
 +             i++, atempo->position[1]++, wa++, wb++) {                  \
 +            float w0 = *wa;                                             \
 +            float w1 = *wb;                                             \
 +            int j;                                                      \
 +                                                                        \
 +            for (j = 0; j < atempo->channels;                           \
 +                 j++, aaa++, bbb++, out++) {                            \
 +                float t0 = (float)*aaa;                                 \
 +                float t1 = (float)*bbb;                                 \
 +                                                                        \
 +                *out =                                                  \
 +                    frag->position[0] + i < 0 ?                         \
 +                    *aaa :                                              \
 +                    (scalar_type)(t0 * w0 + t1 * w1);                   \
 +            }                                                           \
 +        }                                                               \
 +        dst = (uint8_t *)out;                                           \
 +    } while (0)
 +
 +/**
 + * Blend the overlap region of previous and current audio fragment
 + * and output the results to the given destination buffer.
 + *
 + * @return
 + *   0 if the overlap region was completely stored in the dst buffer,
 + *   AVERROR(EAGAIN) if more destination buffer space is required.
 + */
 +static int yae_overlap_add(ATempoContext *atempo,
 +                           uint8_t **dst_ref,
 +                           uint8_t *dst_end)
 +{
 +    // shortcuts:
 +    const AudioFragment *prev = yae_prev_frag(atempo);
 +    const AudioFragment *frag = yae_curr_frag(atempo);
 +
 +    const int64_t start_here = FFMAX(atempo->position[1],
 +                                     frag->position[1]);
 +
 +    const int64_t stop_here = FFMIN(prev->position[1] + prev->nsamples,
 +                                    frag->position[1] + frag->nsamples);
 +
 +    const int64_t overlap = stop_here - start_here;
 +
 +    const int64_t ia = start_here - prev->position[1];
 +    const int64_t ib = start_here - frag->position[1];
 +
 +    const float *wa = atempo->hann + ia;
 +    const float *wb = atempo->hann + ib;
 +
 +    const uint8_t *a = prev->data + ia * atempo->stride;
 +    const uint8_t *b = frag->data + ib * atempo->stride;
 +
 +    uint8_t *dst = *dst_ref;
 +
 +    av_assert0(start_here <= stop_here &&
 +               frag->position[1] <= start_here &&
 +               overlap <= frag->nsamples);
 +
 +    if (atempo->format == AV_SAMPLE_FMT_U8) {
 +        yae_blend(uint8_t);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S16) {
 +        yae_blend(int16_t);
 +    } else if (atempo->format == AV_SAMPLE_FMT_S32) {
 +        yae_blend(int);
 +    } else if (atempo->format == AV_SAMPLE_FMT_FLT) {
 +        yae_blend(float);
 +    } else if (atempo->format == AV_SAMPLE_FMT_DBL) {
 +        yae_blend(double);
 +    }
 +
 +    // pass-back the updated destination buffer pointer:
 +    *dst_ref = dst;
 +
 +    return atempo->position[1] == stop_here ? 0 : AVERROR(EAGAIN);
 +}
 +
 +/**
 + * Feed as much data to the filter as it is able to consume
 + * and receive as much processed data in the destination buffer
 + * as it is able to produce or store.
 + */
 +static void
 +yae_apply(ATempoContext *atempo,
 +          const uint8_t **src_ref,
 +          const uint8_t *src_end,
 +          uint8_t **dst_ref,
 +          uint8_t *dst_end)
 +{
 +    while (1) {
 +        if (atempo->state == YAE_LOAD_FRAGMENT) {
 +            // load additional data for the current fragment:
 +            if (yae_load_frag(atempo, src_ref, src_end) != 0) {
 +                break;
 +            }
 +
 +            // down-mix to mono:
 +            yae_downmix(atempo, yae_curr_frag(atempo));
 +
 +            // apply rDFT:
 +            av_rdft_calc(atempo->real_to_complex, yae_curr_frag(atempo)->xdat);
 +
 +            // must load the second fragment before alignment can start:
 +            if (!atempo->nfrag) {
 +                yae_advance_to_next_frag(atempo);
 +                continue;
 +            }
 +
 +            atempo->state = YAE_ADJUST_POSITION;
 +        }
 +
 +        if (atempo->state == YAE_ADJUST_POSITION) {
 +            // adjust position for better alignment:
 +            if (yae_adjust_position(atempo)) {
 +                // reload the fragment at the corrected position, so that the
 +                // Hann window blending would not require normalization:
 +                atempo->state = YAE_RELOAD_FRAGMENT;
 +            } else {
 +                atempo->state = YAE_OUTPUT_OVERLAP_ADD;
 +            }
 +        }
 +
 +        if (atempo->state == YAE_RELOAD_FRAGMENT) {
 +            // load additional data if necessary due to position adjustment:
 +            if (yae_load_frag(atempo, src_ref, src_end) != 0) {
 +                break;
 +            }
 +
 +            // down-mix to mono:
 +            yae_downmix(atempo, yae_curr_frag(atempo));
 +
 +            // apply rDFT:
 +            av_rdft_calc(atempo->real_to_complex, yae_curr_frag(atempo)->xdat);
 +
 +            atempo->state = YAE_OUTPUT_OVERLAP_ADD;
 +        }
 +
 +        if (atempo->state == YAE_OUTPUT_OVERLAP_ADD) {
 +            // overlap-add and output the result:
 +            if (yae_overlap_add(atempo, dst_ref, dst_end) != 0) {
 +                break;
 +            }
 +
 +            // advance to the next fragment, repeat:
 +            yae_advance_to_next_frag(atempo);
 +            atempo->state = YAE_LOAD_FRAGMENT;
 +        }
 +    }
 +}
 +
 +/**
 + * Flush any buffered data from the filter.
 + *
 + * @return
 + *   0 if all data was completely stored in the dst buffer,
 + *   AVERROR(EAGAIN) if more destination buffer space is required.
 + */
 +static int yae_flush(ATempoContext *atempo,
 +                     uint8_t **dst_ref,
 +                     uint8_t *dst_end)
 +{
 +    AudioFragment *frag = yae_curr_frag(atempo);
 +    int64_t overlap_end;
 +    int64_t start_here;
 +    int64_t stop_here;
 +    int64_t offset;
 +
 +    const uint8_t *src;
 +    uint8_t *dst;
 +
 +    int src_size;
 +    int dst_size;
 +    int nbytes;
 +
 +    atempo->state = YAE_FLUSH_OUTPUT;
 +
 +    if (atempo->position[0] == frag->position[0] + frag->nsamples &&
 +        atempo->position[1] == frag->position[1] + frag->nsamples) {
 +        // the current fragment is already flushed:
 +        return 0;
 +    }
 +
 +    if (frag->position[0] + frag->nsamples < atempo->position[0]) {
 +        // finish loading the current (possibly partial) fragment:
 +        yae_load_frag(atempo, NULL, NULL);
 +
 +        if (atempo->nfrag) {
 +            // down-mix to mono:
 +            yae_downmix(atempo, frag);
 +
 +            // apply rDFT:
 +            av_rdft_calc(atempo->real_to_complex, frag->xdat);
 +
 +            // align current fragment to previous fragment:
 +            if (yae_adjust_position(atempo)) {
 +                // reload the current fragment due to adjusted position:
 +                yae_load_frag(atempo, NULL, NULL);
 +            }
 +        }
 +    }
 +
 +    // flush the overlap region:
 +    overlap_end = frag->position[1] + FFMIN(atempo->window / 2,
 +                                            frag->nsamples);
 +
 +    while (atempo->position[1] < overlap_end) {
 +        if (yae_overlap_add(atempo, dst_ref, dst_end) != 0) {
 +            return AVERROR(EAGAIN);
 +        }
 +    }
 +
 +    // flush the remaininder of the current fragment:
 +    start_here = FFMAX(atempo->position[1], overlap_end);
 +    stop_here  = frag->position[1] + frag->nsamples;
 +    offset     = start_here - frag->position[1];
 +    av_assert0(start_here <= stop_here && frag->position[1] <= start_here);
 +
 +    src = frag->data + offset * atempo->stride;
 +    dst = (uint8_t *)*dst_ref;
 +
 +    src_size = (int)(stop_here - start_here) * atempo->stride;
 +    dst_size = dst_end - dst;
 +    nbytes = FFMIN(src_size, dst_size);
 +
 +    memcpy(dst, src, nbytes);
 +    dst += nbytes;
 +
 +    atempo->position[1] += (nbytes / atempo->stride);
 +
 +    // pass-back the updated destination buffer pointer:
 +    *dst_ref = (uint8_t *)dst;
 +
 +    return atempo->position[1] == stop_here ? 0 : AVERROR(EAGAIN);
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    ATempoContext *atempo = ctx->priv;
 +
 +    // NOTE: this assumes that the caller has memset ctx->priv to 0:
 +    atempo->format = AV_SAMPLE_FMT_NONE;
 +    atempo->tempo  = 1.0;
 +    atempo->state  = YAE_LOAD_FRAGMENT;
 +
 +    return args ? yae_set_tempo(ctx, args) : 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    ATempoContext *atempo = ctx->priv;
 +    yae_release_buffers(atempo);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterChannelLayouts *layouts = NULL;
 +    AVFilterFormats        *formats = NULL;
 +
 +    // WSOLA necessitates an internal sliding window ring buffer
 +    // for incoming audio stream.
 +    //
 +    // Planar sample formats are too cumbersome to store in a ring buffer,
 +    // therefore planar sample formats are not supported.
 +    //
 +    enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_U8,
 +        AV_SAMPLE_FMT_S16,
 +        AV_SAMPLE_FMT_S32,
 +        AV_SAMPLE_FMT_FLT,
 +        AV_SAMPLE_FMT_DBL,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts) {
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats) {
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats) {
 +        return AVERROR(ENOMEM);
 +    }
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static int config_props(AVFilterLink *inlink)
 +{
 +    AVFilterContext  *ctx = inlink->dst;
 +    ATempoContext *atempo = ctx->priv;
 +
 +    enum AVSampleFormat format = inlink->format;
 +    int sample_rate = (int)inlink->sample_rate;
 +    int channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
 +
 +    return yae_reset(atempo, format, sample_rate, channels);
 +}
 +
 +static void push_samples(ATempoContext *atempo,
 +                         AVFilterLink *outlink,
 +                         int n_out)
 +{
 +    atempo->dst_buffer->audio->sample_rate = outlink->sample_rate;
 +    atempo->dst_buffer->audio->nb_samples  = n_out;
 +
 +    // adjust the PTS:
 +    atempo->dst_buffer->pts =
 +        av_rescale_q(atempo->nsamples_out,
 +                     (AVRational){ 1, outlink->sample_rate },
 +                     outlink->time_base);
 +
 +    ff_filter_samples(outlink, atempo->dst_buffer);
 +    atempo->dst_buffer = NULL;
 +    atempo->dst        = NULL;
 +    atempo->dst_end    = NULL;
 +
 +    atempo->nsamples_out += n_out;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink,
 +                           AVFilterBufferRef *src_buffer)
 +{
 +    AVFilterContext  *ctx = inlink->dst;
 +    ATempoContext *atempo = ctx->priv;
 +    AVFilterLink *outlink = ctx->outputs[0];
 +
 +    int n_in = src_buffer->audio->nb_samples;
 +    int n_out = (int)(0.5 + ((double)n_in) / atempo->tempo);
 +
 +    const uint8_t *src = src_buffer->data[0];
 +    const uint8_t *src_end = src + n_in * atempo->stride;
 +
 +    while (src < src_end) {
 +        if (!atempo->dst_buffer) {
 +            atempo->dst_buffer = ff_get_audio_buffer(outlink,
 +                                                     AV_PERM_WRITE,
 +                                                     n_out);
 +            avfilter_copy_buffer_ref_props(atempo->dst_buffer, src_buffer);
 +
 +            atempo->dst = atempo->dst_buffer->data[0];
 +            atempo->dst_end = atempo->dst + n_out * atempo->stride;
 +        }
 +
 +        yae_apply(atempo, &src, src_end, &atempo->dst, atempo->dst_end);
 +
 +        if (atempo->dst == atempo->dst_end) {
 +            push_samples(atempo, outlink, n_out);
 +            atempo->request_fulfilled = 1;
 +        }
 +    }
 +
 +    atempo->nsamples_in += n_in;
 +    avfilter_unref_bufferp(&src_buffer);
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext  *ctx = outlink->src;
 +    ATempoContext *atempo = ctx->priv;
 +    int ret;
 +
 +    atempo->request_fulfilled = 0;
 +    do {
 +        ret = ff_request_frame(ctx->inputs[0]);
 +    }
 +    while (!atempo->request_fulfilled && ret >= 0);
 +
 +    if (ret == AVERROR_EOF) {
 +        // flush the filter:
 +        int n_max = atempo->ring;
 +        int n_out;
 +        int err = AVERROR(EAGAIN);
 +
 +        while (err == AVERROR(EAGAIN)) {
 +            if (!atempo->dst_buffer) {
 +                atempo->dst_buffer = ff_get_audio_buffer(outlink,
 +                                                         AV_PERM_WRITE,
 +                                                         n_max);
 +
 +                atempo->dst = atempo->dst_buffer->data[0];
 +                atempo->dst_end = atempo->dst + n_max * atempo->stride;
 +            }
 +
 +            err = yae_flush(atempo, &atempo->dst, atempo->dst_end);
 +
 +            n_out = ((atempo->dst - atempo->dst_buffer->data[0]) /
 +                     atempo->stride);
 +
 +            if (n_out) {
 +                push_samples(atempo, outlink, n_out);
 +            }
 +        }
 +
 +        avfilter_unref_bufferp(&atempo->dst_buffer);
 +        atempo->dst     = NULL;
 +        atempo->dst_end = NULL;
 +
 +        return AVERROR_EOF;
 +    }
 +
 +    return ret;
 +}
 +
 +static int process_command(AVFilterContext *ctx,
 +                           const char *cmd,
 +                           const char *arg,
 +                           char *res,
 +                           int res_len,
 +                           int flags)
 +{
 +    return !strcmp(cmd, "tempo") ? yae_set_tempo(ctx, arg) : AVERROR(ENOSYS);
 +}
 +
 +AVFilter avfilter_af_atempo = {
 +    .name            = "atempo",
 +    .description     = NULL_IF_CONFIG_SMALL("Adjust audio tempo."),
 +    .init            = init,
 +    .uninit          = uninit,
 +    .query_formats   = query_formats,
 +    .process_command = process_command,
 +    .priv_size       = sizeof(ATempoContext),
 +
 +    .inputs    = (const AVFilterPad[]) {
 +        { .name            = "default",
 +          .type            = AVMEDIA_TYPE_AUDIO,
 +          .filter_samples  = filter_samples,
 +          .config_props    = config_props,
 +          .min_perms       = AV_PERM_READ, },
 +        { .name = NULL}
 +    },
 +
 +    .outputs   = (const AVFilterPad[]) {
 +        { .name            = "default",
 +          .request_frame   = request_frame,
 +          .type            = AVMEDIA_TYPE_AUDIO, },
 +        { .name = NULL}
 +    },
 +};
Simple merge
@@@ -45,9 -45,14 +45,9 @@@ static const AVOption channelsplit_opti
      { NULL },
  };
  
 -static const AVClass channelsplit_class = {
 -    .class_name = "channelsplit filter",
 -    .item_name  = av_default_item_name,
 -    .option     = options,
 -    .version    = LIBAVUTIL_VERSION_INT,
 -};
 +AVFILTER_DEFINE_CLASS(channelsplit);
  
- static int init(AVFilterContext *ctx, const char *arg, void *opaque)
+ static int init(AVFilterContext *ctx, const char *arg)
  {
      ChannelSplitContext *s = ctx->priv;
      int nb_channels;
index 052bf7d,0000000..f451e00
mode 100644,000000..100644
--- /dev/null
@@@ -1,388 -1,0 +1,388 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
 +/*
 + * Copyright (c) 2002 Anders Johansson <ajh@atri.curtin.edu.au>
 + * Copyright (c) 2011 Clément Bœsch <ubitux@gmail.com>
 + * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Audio panning filter (channels mixing)
 + * Original code written by Anders Johansson for MPlayer,
 + * reimplemented for FFmpeg.
 + */
 +
 +#include <stdio.h>
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/opt.h"
 +#include "libswresample/swresample.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "formats.h"
 +#include "internal.h"
 +
 +#define MAX_CHANNELS 63
 +
 +typedef struct PanContext {
 +    int64_t out_channel_layout;
 +    double gain[MAX_CHANNELS][MAX_CHANNELS];
 +    int64_t need_renorm;
 +    int need_renumber;
 +    int nb_input_channels;
 +    int nb_output_channels;
 +
 +    int pure_gains;
 +    /* channel mapping specific */
 +    int channel_map[SWR_CH_MAX];
 +    struct SwrContext *swr;
 +} PanContext;
 +
 +static int parse_channel_name(char **arg, int *rchannel, int *rnamed)
 +{
 +    char buf[8];
 +    int len, i, channel_id = 0;
 +    int64_t layout, layout0;
 +
 +    /* try to parse a channel name, e.g. "FL" */
 +    if (sscanf(*arg, " %7[A-Z] %n", buf, &len)) {
 +        layout0 = layout = av_get_channel_layout(buf);
 +        /* channel_id <- first set bit in layout */
 +        for (i = 32; i > 0; i >>= 1) {
 +            if (layout >= (int64_t)1 << i) {
 +                channel_id += i;
 +                layout >>= i;
 +            }
 +        }
 +        /* reject layouts that are not a single channel */
 +        if (channel_id >= MAX_CHANNELS || layout0 != (int64_t)1 << channel_id)
 +            return AVERROR(EINVAL);
 +        *rchannel = channel_id;
 +        *rnamed = 1;
 +        *arg += len;
 +        return 0;
 +    }
 +    /* try to parse a channel number, e.g. "c2" */
 +    if (sscanf(*arg, " c%d %n", &channel_id, &len) &&
 +        channel_id >= 0 && channel_id < MAX_CHANNELS) {
 +        *rchannel = channel_id;
 +        *rnamed = 0;
 +        *arg += len;
 +        return 0;
 +    }
 +    return AVERROR(EINVAL);
 +}
 +
 +static void skip_spaces(char **arg)
 +{
 +    int len = 0;
 +
 +    sscanf(*arg, " %n", &len);
 +    *arg += len;
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args0)
 +{
 +    PanContext *const pan = ctx->priv;
 +    char *arg, *arg0, *tokenizer, *args = av_strdup(args0);
 +    int out_ch_id, in_ch_id, len, named, ret;
 +    int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels
 +    double gain;
 +
 +    if (!args0) {
 +        av_log(ctx, AV_LOG_ERROR,
 +               "pan filter needs a channel layout and a set "
 +               "of channels definitions as parameter\n");
 +        return AVERROR(EINVAL);
 +    }
 +    if (!args)
 +        return AVERROR(ENOMEM);
 +    arg = av_strtok(args, ":", &tokenizer);
 +    ret = ff_parse_channel_layout(&pan->out_channel_layout, arg, ctx);
 +    if (ret < 0)
 +        return ret;
 +    pan->nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channel_layout);
 +
 +    /* parse channel specifications */
 +    while ((arg = arg0 = av_strtok(NULL, ":", &tokenizer))) {
 +        /* channel name */
 +        if (parse_channel_name(&arg, &out_ch_id, &named)) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Expected out channel name, got \"%.8s\"\n", arg);
 +            return AVERROR(EINVAL);
 +        }
 +        if (named) {
 +            if (!((pan->out_channel_layout >> out_ch_id) & 1)) {
 +                av_log(ctx, AV_LOG_ERROR,
 +                       "Channel \"%.8s\" does not exist in the chosen layout\n", arg0);
 +                return AVERROR(EINVAL);
 +            }
 +            /* get the channel number in the output channel layout:
 +             * out_channel_layout & ((1 << out_ch_id) - 1) are all the
 +             * channels that come before out_ch_id,
 +             * so their count is the index of out_ch_id */
 +            out_ch_id = av_get_channel_layout_nb_channels(pan->out_channel_layout & (((int64_t)1 << out_ch_id) - 1));
 +        }
 +        if (out_ch_id < 0 || out_ch_id >= pan->nb_output_channels) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Invalid out channel name \"%.8s\"\n", arg0);
 +            return AVERROR(EINVAL);
 +        }
 +        if (*arg == '=') {
 +            arg++;
 +        } else if (*arg == '<') {
 +            pan->need_renorm |= (int64_t)1 << out_ch_id;
 +            arg++;
 +        } else {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Syntax error after channel name in \"%.8s\"\n", arg0);
 +            return AVERROR(EINVAL);
 +        }
 +        /* gains */
 +        while (1) {
 +            gain = 1;
 +            if (sscanf(arg, " %lf %n* %n", &gain, &len, &len))
 +                arg += len;
 +            if (parse_channel_name(&arg, &in_ch_id, &named)){
 +                av_log(ctx, AV_LOG_ERROR,
 +                       "Expected in channel name, got \"%.8s\"\n", arg);
 +                return AVERROR(EINVAL);
 +            }
 +            nb_in_channels[named]++;
 +            if (nb_in_channels[!named]) {
 +                av_log(ctx, AV_LOG_ERROR,
 +                       "Can not mix named and numbered channels\n");
 +                return AVERROR(EINVAL);
 +            }
 +            pan->gain[out_ch_id][in_ch_id] = gain;
 +            if (!*arg)
 +                break;
 +            if (*arg != '+') {
 +                av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg);
 +                return AVERROR(EINVAL);
 +            }
 +            arg++;
 +            skip_spaces(&arg);
 +        }
 +    }
 +    pan->need_renumber = !!nb_in_channels[1];
 +
 +    av_free(args);
 +    return 0;
 +}
 +
 +static int are_gains_pure(const PanContext *pan)
 +{
 +    int i, j;
 +
 +    for (i = 0; i < MAX_CHANNELS; i++) {
 +        int nb_gain = 0;
 +
 +        for (j = 0; j < MAX_CHANNELS; j++) {
 +            double gain = pan->gain[i][j];
 +
 +            /* channel mapping is effective only if 0% or 100% of a channel is
 +             * selected... */
 +            if (gain != 0. && gain != 1.)
 +                return 0;
 +            /* ...and if the output channel is only composed of one input */
 +            if (gain && nb_gain++)
 +                return 0;
 +        }
 +    }
 +    return 1;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    PanContext *pan = ctx->priv;
 +    AVFilterLink *inlink  = ctx->inputs[0];
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    AVFilterFormats *formats = NULL;
 +    AVFilterChannelLayouts *layouts;
 +
 +    pan->pure_gains = are_gains_pure(pan);
 +    /* libswr supports any sample and packing formats */
 +    ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO));
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    // inlink supports any channel layout
 +    layouts = ff_all_channel_layouts();
 +    ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
 +
 +    // outlink supports only requested output channel layout
 +    layouts = NULL;
 +    ff_add_channel_layout(&layouts, pan->out_channel_layout);
 +    ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
 +    return 0;
 +}
 +
 +static int config_props(AVFilterLink *link)
 +{
 +    AVFilterContext *ctx = link->dst;
 +    PanContext *pan = ctx->priv;
 +    char buf[1024], *cur;
 +    int i, j, k, r;
 +    double t;
 +
 +    pan->nb_input_channels = av_get_channel_layout_nb_channels(link->channel_layout);
 +    if (pan->need_renumber) {
 +        // input channels were given by their name: renumber them
 +        for (i = j = 0; i < MAX_CHANNELS; i++) {
 +            if ((link->channel_layout >> i) & 1) {
 +                for (k = 0; k < pan->nb_output_channels; k++)
 +                    pan->gain[k][j] = pan->gain[k][i];
 +                j++;
 +            }
 +        }
 +    }
 +
 +    // sanity check; can't be done in query_formats since the inlink
 +    // channel layout is unknown at that time
 +    if (pan->nb_input_channels > SWR_CH_MAX ||
 +        pan->nb_output_channels > SWR_CH_MAX) {
 +        av_log(ctx, AV_LOG_ERROR,
 +               "libswresample support a maximum of %d channels. "
 +               "Feel free to ask for a higher limit.\n", SWR_CH_MAX);
 +        return AVERROR_PATCHWELCOME;
 +    }
 +
 +    // init libswresample context
 +    pan->swr = swr_alloc_set_opts(pan->swr,
 +                                  pan->out_channel_layout, link->format, link->sample_rate,
 +                                  link->channel_layout,    link->format, link->sample_rate,
 +                                  0, ctx);
 +    if (!pan->swr)
 +        return AVERROR(ENOMEM);
 +
 +    // gains are pure, init the channel mapping
 +    if (pan->pure_gains) {
 +
 +        // get channel map from the pure gains
 +        for (i = 0; i < pan->nb_output_channels; i++) {
 +            int ch_id = -1;
 +            for (j = 0; j < pan->nb_input_channels; j++) {
 +                if (pan->gain[i][j]) {
 +                    ch_id = j;
 +                    break;
 +                }
 +            }
 +            pan->channel_map[i] = ch_id;
 +        }
 +
 +        av_opt_set_int(pan->swr, "icl", pan->out_channel_layout, 0);
 +        av_opt_set_int(pan->swr, "uch", pan->nb_output_channels, 0);
 +        swr_set_channel_mapping(pan->swr, pan->channel_map);
 +    } else {
 +        // renormalize
 +        for (i = 0; i < pan->nb_output_channels; i++) {
 +            if (!((pan->need_renorm >> i) & 1))
 +                continue;
 +            t = 0;
 +            for (j = 0; j < pan->nb_input_channels; j++)
 +                t += pan->gain[i][j];
 +            if (t > -1E-5 && t < 1E-5) {
 +                // t is almost 0 but not exactly, this is probably a mistake
 +                if (t)
 +                    av_log(ctx, AV_LOG_WARNING,
 +                           "Degenerate coefficients while renormalizing\n");
 +                continue;
 +            }
 +            for (j = 0; j < pan->nb_input_channels; j++)
 +                pan->gain[i][j] /= t;
 +        }
 +        av_opt_set_int(pan->swr, "icl", link->channel_layout, 0);
 +        av_opt_set_int(pan->swr, "ocl", pan->out_channel_layout, 0);
 +        swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]);
 +    }
 +
 +    r = swr_init(pan->swr);
 +    if (r < 0)
 +        return r;
 +
 +    // summary
 +    for (i = 0; i < pan->nb_output_channels; i++) {
 +        cur = buf;
 +        for (j = 0; j < pan->nb_input_channels; j++) {
 +            r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d",
 +                         j ? " + " : "", pan->gain[i][j], j);
 +            cur += FFMIN(buf + sizeof(buf) - cur, r);
 +        }
 +        av_log(ctx, AV_LOG_INFO, "o%d = %s\n", i, buf);
 +    }
 +    // add channel mapping summary if possible
 +    if (pan->pure_gains) {
 +        av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:");
 +        for (i = 0; i < pan->nb_output_channels; i++)
 +            if (pan->channel_map[i] < 0)
 +                av_log(ctx, AV_LOG_INFO, " M");
 +            else
 +                av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]);
 +        av_log(ctx, AV_LOG_INFO, "\n");
 +        return 0;
 +    }
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 +{
 +    int n = insamples->audio->nb_samples;
 +    AVFilterLink *const outlink = inlink->dst->outputs[0];
 +    AVFilterBufferRef *outsamples = ff_get_audio_buffer(outlink, AV_PERM_WRITE, n);
 +    PanContext *pan = inlink->dst->priv;
 +
 +    swr_convert(pan->swr, outsamples->data, n, (void *)insamples->data, n);
 +    avfilter_copy_buffer_ref_props(outsamples, insamples);
 +    outsamples->audio->channel_layout = outlink->channel_layout;
 +
 +    ff_filter_samples(outlink, outsamples);
 +    avfilter_unref_buffer(insamples);
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    PanContext *pan = ctx->priv;
 +    swr_free(&pan->swr);
 +}
 +
 +AVFilter avfilter_af_pan = {
 +    .name          = "pan",
 +    .description   = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."),
 +    .priv_size     = sizeof(PanContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .config_props     = config_props,
 +          .filter_samples   = filter_samples,
 +          .min_perms        = AV_PERM_READ, },
 +        { .name = NULL}
 +    },
 +    .outputs   = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_AUDIO, },
 +        { .name = NULL}
 +    },
 +};
index 5ef5f96,0000000..724a923
mode 100644,000000..100644
--- /dev/null
@@@ -1,170 -1,0 +1,170 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2012 Clément Bœsch <ubitux@gmail.com>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Audio silence detector
 + */
 +
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/timestamp.h"
 +#include "audio.h"
 +#include "formats.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    char *noise_str;            ///< noise option string
 +    double noise;               ///< noise amplitude ratio
 +    int duration;               ///< minimum duration of silence until notification
 +    int64_t nb_null_samples;    ///< current number of continuous zero samples
 +    int64_t start;              ///< if silence is detected, this value contains the time of the first zero sample
 +    int last_sample_rate;       ///< last sample rate to check for sample rate changes
 +} SilenceDetectContext;
 +
 +#define OFFSET(x) offsetof(SilenceDetectContext, x)
 +static const AVOption silencedetect_options[] = {
 +    { "n",         "set noise tolerance",              OFFSET(noise_str), AV_OPT_TYPE_STRING, {.str="-60dB"}, CHAR_MIN, CHAR_MAX },
 +    { "noise",     "set noise tolerance",              OFFSET(noise_str), AV_OPT_TYPE_STRING, {.str="-60dB"}, CHAR_MIN, CHAR_MAX },
 +    { "d",         "set minimum duration in seconds",  OFFSET(duration),  AV_OPT_TYPE_INT,    {.dbl=2},    0, INT_MAX},
 +    { "duration",  "set minimum duration in seconds",  OFFSET(duration),  AV_OPT_TYPE_INT,    {.dbl=2},    0, INT_MAX},
 +    { NULL },
 +};
 +
 +AVFILTER_DEFINE_CLASS(silencedetect);
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    int ret;
 +    char *tail;
 +    SilenceDetectContext *silence = ctx->priv;
 +
 +    silence->class = &silencedetect_class;
 +    av_opt_set_defaults(silence);
 +
 +    if ((ret = av_set_options_string(silence, args, "=", ":")) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
 +        return ret;
 +    }
 +
 +    silence->noise = strtod(silence->noise_str, &tail);
 +    if (!strcmp(tail, "dB")) {
 +        silence->noise = pow(10, silence->noise/20);
 +    } else if (*tail) {
 +        av_log(ctx, AV_LOG_ERROR, "Invalid value '%s' for noise parameter.\n",
 +               silence->noise_str);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 +{
 +    int i;
 +    SilenceDetectContext *silence = inlink->dst->priv;
 +    const int nb_channels           = av_get_channel_layout_nb_channels(inlink->channel_layout);
 +    const int srate                 = inlink->sample_rate;
 +    const int nb_samples            = insamples->audio->nb_samples * nb_channels;
 +    const int64_t nb_samples_notify = srate * silence->duration    * nb_channels;
 +
 +    // scale number of null samples to the new sample rate
 +    if (silence->last_sample_rate && silence->last_sample_rate != srate)
 +        silence->nb_null_samples =
 +            srate * silence->nb_null_samples / silence->last_sample_rate;
 +    silence->last_sample_rate = srate;
 +
 +    // TODO: support more sample formats
 +    if (insamples->format == AV_SAMPLE_FMT_DBL) {
 +        double *p = (double *)insamples->data[0];
 +
 +        for (i = 0; i < nb_samples; i++, p++) {
 +            if (*p < silence->noise && *p > -silence->noise) {
 +                if (!silence->start) {
 +                    silence->nb_null_samples++;
 +                    if (silence->nb_null_samples >= nb_samples_notify) {
 +                        silence->start = insamples->pts - silence->duration / av_q2d(inlink->time_base);
 +                        av_log(silence, AV_LOG_INFO,
 +                               "silence_start: %s\n", av_ts2timestr(silence->start, &inlink->time_base));
 +                    }
 +                }
 +            } else {
 +                if (silence->start)
 +                    av_log(silence, AV_LOG_INFO,
 +                           "silence_end: %s | silence_duration: %s\n",
 +                           av_ts2timestr(insamples->pts,                  &inlink->time_base),
 +                           av_ts2timestr(insamples->pts - silence->start, &inlink->time_base));
 +                silence->nb_null_samples = silence->start = 0;
 +            }
 +        }
 +    }
 +
 +    ff_filter_samples(inlink->dst->outputs[0], insamples);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats = NULL;
 +    AVFilterChannelLayouts *layouts = NULL;
 +    enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_DBL,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +AVFilter avfilter_af_silencedetect = {
 +    .name          = "silencedetect",
 +    .description   = NULL_IF_CONFIG_SMALL("Detect silence."),
 +    .priv_size     = sizeof(SilenceDetectContext),
 +    .init          = init,
 +    .query_formats = query_formats,
 +
 +    .inputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_AUDIO,
 +          .get_audio_buffer = ff_null_get_audio_buffer,
 +          .filter_samples   = filter_samples, },
 +        { .name = NULL }
 +    },
 +    .outputs = (const AVFilterPad[]) {
 +        { .name = "default",
 +          .type = AVMEDIA_TYPE_AUDIO, },
 +        { .name = NULL }
 +    },
 +};
index e01453b,0000000..11da226
mode 100644,000000..100644
--- /dev/null
@@@ -1,191 -1,0 +1,191 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * audio volume filter
 + * based on ffmpeg.c code
 + */
 +
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/eval.h"
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "formats.h"
 +
 +typedef struct {
 +    double volume;
 +    int    volume_i;
 +} VolumeContext;
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    VolumeContext *vol = ctx->priv;
 +    char *tail;
 +    int ret = 0;
 +
 +    vol->volume = 1.0;
 +
 +    if (args) {
 +        /* parse the number as a decimal number */
 +        double d = strtod(args, &tail);
 +
 +        if (*tail) {
 +            if (!strcmp(tail, "dB")) {
 +                /* consider the argument an adjustement in decibels */
 +                d = pow(10, d/20);
 +            } else {
 +                /* parse the argument as an expression */
 +                ret = av_expr_parse_and_eval(&d, args, NULL, NULL,
 +                                             NULL, NULL, NULL, NULL,
 +                                             NULL, 0, ctx);
 +            }
 +        }
 +
 +        if (ret < 0) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Invalid volume argument '%s'\n", args);
 +            return AVERROR(EINVAL);
 +        }
 +
 +        if (d < 0 || d > 65536) { /* 65536 = INT_MIN / (128 * 256) */
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Negative or too big volume value %f\n", d);
 +            return AVERROR(EINVAL);
 +        }
 +
 +        vol->volume = d;
 +    }
 +
 +    vol->volume_i = (int)(vol->volume * 256 + 0.5);
 +    av_log(ctx, AV_LOG_INFO, "volume=%f\n", vol->volume);
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *formats = NULL;
 +    AVFilterChannelLayouts *layouts;
 +    enum AVSampleFormat sample_fmts[] = {
 +        AV_SAMPLE_FMT_U8,
 +        AV_SAMPLE_FMT_S16,
 +        AV_SAMPLE_FMT_S32,
 +        AV_SAMPLE_FMT_FLT,
 +        AV_SAMPLE_FMT_DBL,
 +        AV_SAMPLE_FMT_NONE
 +    };
 +
 +    layouts = ff_all_channel_layouts();
 +    if (!layouts)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +
 +    formats = ff_make_format_list(sample_fmts);
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +
 +    formats = ff_all_samplerates();
 +    if (!formats)
 +        return AVERROR(ENOMEM);
 +    ff_set_common_samplerates(ctx, formats);
 +
 +    return 0;
 +}
 +
 +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 +{
 +    VolumeContext *vol = inlink->dst->priv;
 +    AVFilterLink *outlink = inlink->dst->outputs[0];
 +    const int nb_samples = insamples->audio->nb_samples *
 +        av_get_channel_layout_nb_channels(insamples->audio->channel_layout);
 +    const double volume   = vol->volume;
 +    const int    volume_i = vol->volume_i;
 +    int i;
 +
 +    if (volume_i != 256) {
 +        switch (insamples->format) {
 +        case AV_SAMPLE_FMT_U8:
 +        {
 +            uint8_t *p = (void *)insamples->data[0];
 +            for (i = 0; i < nb_samples; i++) {
 +                int v = (((*p - 128) * volume_i + 128) >> 8) + 128;
 +                *p++ = av_clip_uint8(v);
 +            }
 +            break;
 +        }
 +        case AV_SAMPLE_FMT_S16:
 +        {
 +            int16_t *p = (void *)insamples->data[0];
 +            for (i = 0; i < nb_samples; i++) {
 +                int v = ((int64_t)*p * volume_i + 128) >> 8;
 +                *p++ = av_clip_int16(v);
 +            }
 +            break;
 +        }
 +        case AV_SAMPLE_FMT_S32:
 +        {
 +            int32_t *p = (void *)insamples->data[0];
 +            for (i = 0; i < nb_samples; i++) {
 +                int64_t v = (((int64_t)*p * volume_i + 128) >> 8);
 +                *p++ = av_clipl_int32(v);
 +            }
 +            break;
 +        }
 +        case AV_SAMPLE_FMT_FLT:
 +        {
 +            float *p = (void *)insamples->data[0];
 +            float scale = (float)volume;
 +            for (i = 0; i < nb_samples; i++) {
 +                *p++ *= scale;
 +            }
 +            break;
 +        }
 +        case AV_SAMPLE_FMT_DBL:
 +        {
 +            double *p = (void *)insamples->data[0];
 +            for (i = 0; i < nb_samples; i++) {
 +                *p *= volume;
 +                p++;
 +            }
 +            break;
 +        }
 +        }
 +    }
 +    ff_filter_samples(outlink, insamples);
 +}
 +
 +AVFilter avfilter_af_volume = {
 +    .name           = "volume",
 +    .description    = NULL_IF_CONFIG_SMALL("Change input volume."),
 +    .query_formats  = query_formats,
 +    .priv_size      = sizeof(VolumeContext),
 +    .init           = init,
 +
 +    .inputs  = (const AVFilterPad[])  {{ .name     = "default",
 +                                   .type           = AVMEDIA_TYPE_AUDIO,
 +                                   .filter_samples = filter_samples,
 +                                   .min_perms      = AV_PERM_READ|AV_PERM_WRITE},
 +                                 { .name = NULL}},
 +
 +    .outputs = (const AVFilterPad[])  {{ .name     = "default",
 +                                   .type           = AVMEDIA_TYPE_AUDIO, },
 +                                 { .name = NULL}},
 +};
index ec58ab3,0000000..dfb6b5a
mode 100644,000000..100644
--- /dev/null
@@@ -1,254 -1,0 +1,254 @@@
- static int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * eval audio source
 + */
 +
 +#include "libavutil/audioconvert.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/eval.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/parseutils.h"
 +#include "avfilter.h"
 +#include "audio.h"
 +#include "internal.h"
 +
 +static const char * const var_names[] = {
 +    "n",            ///< number of frame
 +    "t",            ///< timestamp expressed in seconds
 +    "s",            ///< sample rate
 +    NULL
 +};
 +
 +enum var_name {
 +    VAR_N,
 +    VAR_T,
 +    VAR_S,
 +    VAR_VARS_NB
 +};
 +
 +typedef struct {
 +    const AVClass *class;
 +    char *sample_rate_str;
 +    int sample_rate;
 +    int64_t chlayout;
 +    char *chlayout_str;
 +    int nb_channels;
 +    int64_t pts;
 +    AVExpr *expr[8];
 +    char *expr_str[8];
 +    int nb_samples;             ///< number of samples per requested frame
 +    char *duration_str;         ///< total duration of the generated audio
 +    double duration;
 +    uint64_t n;
 +    double var_values[VAR_VARS_NB];
 +} EvalContext;
 +
 +#define OFFSET(x) offsetof(EvalContext, x)
 +
 +static const AVOption aevalsrc_options[]= {
 +    { "nb_samples",  "set the number of samples per requested frame", OFFSET(nb_samples),      AV_OPT_TYPE_INT,    {.dbl = 1024},    0,        INT_MAX },
 +    { "n",           "set the number of samples per requested frame", OFFSET(nb_samples),      AV_OPT_TYPE_INT,    {.dbl = 1024},    0,        INT_MAX },
 +    { "sample_rate", "set the sample rate",                           OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX },
 +    { "s",           "set the sample rate",                           OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX },
 +    { "duration",    "set audio duration", OFFSET(duration_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
 +    { "d",           "set audio duration", OFFSET(duration_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
 +    { "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
 +    { "c",              "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
 +{NULL},
 +};
 +
 +AVFILTER_DEFINE_CLASS(aevalsrc);
 +
++static int init(AVFilterContext *ctx, const char *args)
 +{
 +    EvalContext *eval = ctx->priv;
 +    char *args1 = av_strdup(args);
 +    char *expr, *buf, *bufptr;
 +    int ret, i;
 +
 +    eval->class = &aevalsrc_class;
 +    av_opt_set_defaults(eval);
 +
 +    /* parse expressions */
 +    buf = args1;
 +    i = 0;
 +    while (expr = av_strtok(buf, ":", &bufptr)) {
 +        ret = av_expr_parse(&eval->expr[i], expr, var_names,
 +                            NULL, NULL, NULL, NULL, 0, ctx);
 +        if (ret < 0)
 +            goto end;
 +        i++;
 +        if (bufptr && *bufptr == ':') { /* found last expression */
 +            bufptr++;
 +            break;
 +        }
 +        buf = NULL;
 +    }
 +    eval->nb_channels = i;
 +
 +    if (bufptr && (ret = av_set_options_string(eval, bufptr, "=", ":")) < 0)
 +        goto end;
 +
 +    if (eval->chlayout_str) {
 +        int n;
 +        ret = ff_parse_channel_layout(&eval->chlayout, eval->chlayout_str, ctx);
 +        if (ret < 0)
 +            goto end;
 +
 +        n = av_get_channel_layout_nb_channels(eval->chlayout);
 +        if (n != eval->nb_channels) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Mismatch between the specified number of channels '%d' "
 +                   "and the number of channels '%d' in the specified channel layout '%s'\n",
 +                   eval->nb_channels, n, eval->chlayout_str);
 +            ret = AVERROR(EINVAL);
 +            goto end;
 +        }
 +    } else {
 +        /* guess channel layout from nb expressions/channels */
 +        eval->chlayout = av_get_default_channel_layout(eval->nb_channels);
 +        if (!eval->chlayout) {
 +            av_log(ctx, AV_LOG_ERROR, "Invalid number of channels '%d' provided\n",
 +                   eval->nb_channels);
 +            ret = AVERROR(EINVAL);
 +            goto end;
 +        }
 +    }
 +
 +    if ((ret = ff_parse_sample_rate(&eval->sample_rate, eval->sample_rate_str, ctx)))
 +        goto end;
 +
 +    eval->duration = -1;
 +    if (eval->duration_str) {
 +        int64_t us = -1;
 +        if ((ret = av_parse_time(&us, eval->duration_str, 1)) < 0) {
 +            av_log(ctx, AV_LOG_ERROR, "Invalid duration: '%s'\n", eval->duration_str);
 +            goto end;
 +        }
 +        eval->duration = (double)us / 1000000;
 +    }
 +    eval->n = 0;
 +
 +end:
 +    av_free(args1);
 +    return ret;
 +}
 +
 +static void uninit(AVFilterContext *ctx)
 +{
 +    EvalContext *eval = ctx->priv;
 +    int i;
 +
 +    for (i = 0; i < 8; i++) {
 +        av_expr_free(eval->expr[i]);
 +        eval->expr[i] = NULL;
 +    }
 +    av_freep(&eval->chlayout_str);
 +    av_freep(&eval->duration_str);
 +    av_freep(&eval->sample_rate_str);
 +}
 +
 +static int config_props(AVFilterLink *outlink)
 +{
 +    EvalContext *eval = outlink->src->priv;
 +    char buf[128];
 +
 +    outlink->time_base = (AVRational){1, eval->sample_rate};
 +    outlink->sample_rate = eval->sample_rate;
 +
 +    eval->var_values[VAR_S] = eval->sample_rate;
 +
 +    av_get_channel_layout_string(buf, sizeof(buf), 0, eval->chlayout);
 +
 +    av_log(outlink->src, AV_LOG_INFO,
 +           "sample_rate:%d chlayout:%s duration:%f\n",
 +           eval->sample_rate, buf, eval->duration);
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    EvalContext *eval = ctx->priv;
 +    enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE };
 +    int64_t chlayouts[] = { eval->chlayout, -1 };
 +    int sample_rates[] = { eval->sample_rate, -1 };
 +
 +    ff_set_common_formats (ctx, ff_make_format_list(sample_fmts));
 +    ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts));
 +    ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
 +
 +    return 0;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    EvalContext *eval = outlink->src->priv;
 +    AVFilterBufferRef *samplesref;
 +    int i, j;
 +    double t = eval->var_values[VAR_N] * (double)1/eval->sample_rate;
 +
 +    if (eval->duration >= 0 && t > eval->duration)
 +        return AVERROR_EOF;
 +
 +    samplesref = ff_get_audio_buffer(outlink, AV_PERM_WRITE, eval->nb_samples);
 +
 +    /* evaluate expression for each single sample and for each channel */
 +    for (i = 0; i < eval->nb_samples; i++, eval->n++) {
 +        eval->var_values[VAR_N] = eval->n;
 +        eval->var_values[VAR_T] = eval->var_values[VAR_N] * (double)1/eval->sample_rate;
 +
 +        for (j = 0; j < eval->nb_channels; j++) {
 +            *((double *) samplesref->extended_data[j] + i) =
 +                av_expr_eval(eval->expr[j], eval->var_values, NULL);
 +        }
 +    }
 +
 +    samplesref->pts = eval->pts;
 +    samplesref->pos = -1;
 +    samplesref->audio->sample_rate = eval->sample_rate;
 +    eval->pts += eval->nb_samples;
 +
 +    ff_filter_samples(outlink, samplesref);
 +
 +    return 0;
 +}
 +
 +AVFilter avfilter_asrc_aevalsrc = {
 +    .name        = "aevalsrc",
 +    .description = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
 +
 +    .query_formats = query_formats,
 +    .init        = init,
 +    .uninit      = uninit,
 +    .priv_size   = sizeof(EvalContext),
 +
 +    .inputs      = (const AVFilterPad[]) {{ .name = NULL}},
 +
 +    .outputs     = (const AVFilterPad[]) {{ .name = "default",
 +                                      .type = AVMEDIA_TYPE_AUDIO,
 +                                      .config_props = config_props,
 +                                      .request_frame = request_frame, },
 +                                    { .name = NULL}},
 +};
   * null audio source
   */
  
 -#include "avfilter.h"
  #include "internal.h"
  #include "libavutil/audioconvert.h"
 +#include "libavutil/opt.h"
 +
 +#include "audio.h"
 +#include "avfilter.h"
 +#include "internal.h"
  
  typedef struct {
 +    const AVClass *class;
 +    char   *channel_layout_str;
      uint64_t channel_layout;
 -    int64_t sample_rate;
 +    char   *sample_rate_str;
 +    int     sample_rate;
 +    int nb_samples;             ///< number of samples per requested frame
 +    int64_t pts;
  } ANullContext;
  
- static int init(AVFilterContext *ctx, const char *args, void *opaque)
 +#define OFFSET(x) offsetof(ANullContext, x)
 +
 +static const AVOption anullsrc_options[]= {
 +    { "channel_layout", "set channel_layout", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0 },
 +    { "cl",             "set channel_layout", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0 },
 +    { "sample_rate",    "set sample rate",    OFFSET(sample_rate_str)   , AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0 },
 +    { "r",              "set sample rate",    OFFSET(sample_rate_str)   , AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0 },
 +    { "nb_samples",     "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.dbl = 1024}, 0, INT_MAX },
 +    { "n",              "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.dbl = 1024}, 0, INT_MAX },
 +    { NULL },
 +};
 +
 +AVFILTER_DEFINE_CLASS(anullsrc);
 +
+ static int init(AVFilterContext *ctx, const char *args)
  {
 -    ANullContext *priv = ctx->priv;
 -    char channel_layout_str[128] = "";
 -
 -    priv->sample_rate = 44100;
 -    priv->channel_layout = AV_CH_LAYOUT_STEREO;
 +    ANullContext *null = ctx->priv;
 +    int ret;
  
 -    if (args)
 -        sscanf(args, "%"PRId64":%s", &priv->sample_rate, channel_layout_str);
 +    null->class = &anullsrc_class;
 +    av_opt_set_defaults(null);
  
 -    if (priv->sample_rate < 0) {
 -        av_log(ctx, AV_LOG_ERROR, "Invalid negative sample rate: %"PRId64"\n", priv->sample_rate);
 -        return AVERROR(EINVAL);
 +    if ((ret = (av_set_options_string(null, args, "=", ":"))) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
 +        return ret;
      }
  
 -    if (*channel_layout_str)
 -        if (!(priv->channel_layout = av_get_channel_layout(channel_layout_str))
 -            && sscanf(channel_layout_str, "%"PRId64, &priv->channel_layout) != 1) {
 -            av_log(ctx, AV_LOG_ERROR, "Invalid value '%s' for channel layout\n",
 -                   channel_layout_str);
 -            return AVERROR(EINVAL);
 -        }
 +    if ((ret = ff_parse_sample_rate(&null->sample_rate,
 +                                     null->sample_rate_str, ctx)) < 0)
 +        return ret;
 +
 +    if ((ret = ff_parse_channel_layout(&null->channel_layout,
 +                                        null->channel_layout_str, ctx)) < 0)
 +        return ret;
  
      return 0;
  }
Simple merge
Simple merge
@@@ -188,89 -173,38 +188,89 @@@ int av_buffersrc_add_ref(AVFilterContex
      return 0;
  }
  
- static av_cold int init_video(AVFilterContext *ctx, const char *args, void *opaque)
 +int av_buffersrc_buffer(AVFilterContext *s, AVFilterBufferRef *buf)
 +{
 +    return av_buffersrc_add_ref(s, buf, AV_BUFFERSRC_FLAG_NO_COPY);
 +}
 +
 +unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
 +{
 +    return ((BufferSourceContext *)buffer_src->priv)->nb_failed_requests;
 +}
 +
 +#define OFFSET(x) offsetof(BufferSourceContext, x)
 +#define V AV_OPT_FLAG_VIDEO_PARAM
 +static const AVOption buffer_options[] = {
 +    { "time_base",      NULL, OFFSET(time_base),           AV_OPT_TYPE_RATIONAL,   { 0 }, 0, INT_MAX, V },
 +    { "frame_rate",     NULL, OFFSET(frame_rate),          AV_OPT_TYPE_RATIONAL,   { 0 }, 0, INT_MAX, V },
 +    { "video_size",     NULL, OFFSET(w),                   AV_OPT_TYPE_IMAGE_SIZE,           .flags = V },
 +    { "pix_fmt",        NULL, OFFSET(pix_fmt),             AV_OPT_TYPE_PIXEL_FMT,            .flags = V },
 +    { "pixel_aspect",   NULL, OFFSET(pixel_aspect),        AV_OPT_TYPE_RATIONAL,   { 0 }, 0, INT_MAX, V },
 +    { "sws_param",      NULL, OFFSET(sws_param),           AV_OPT_TYPE_STRING,               .flags = V },
 +    { NULL },
 +};
 +#undef V
 +
 +AVFILTER_DEFINE_CLASS(buffer);
 +
+ static av_cold int init_video(AVFilterContext *ctx, const char *args)
  {
      BufferSourceContext *c = ctx->priv;
 -    char pix_fmt_str[128];
 -    int n = 0;
 +    char pix_fmt_str[128], sws_param[256] = "", *colon, *equal;
 +    int ret, n = 0;
  
 -    if (!args ||
 -        (n = sscanf(args, "%d:%d:%127[^:]:%d:%d:%d:%d", &c->w, &c->h, pix_fmt_str,
 -                    &c->time_base.num, &c->time_base.den,
 -                    &c->pixel_aspect.num, &c->pixel_aspect.den)) != 7) {
 -        av_log(ctx, AV_LOG_ERROR, "Expected 7 arguments, but %d found in '%s'\n", n, args);
 +    c->class = &buffer_class;
 +
 +    if (!args) {
 +        av_log(ctx, AV_LOG_ERROR, "Arguments required\n");
          return AVERROR(EINVAL);
      }
 -    if ((c->pix_fmt = av_get_pix_fmt(pix_fmt_str)) == PIX_FMT_NONE) {
 -        char *tail;
 -        c->pix_fmt = strtol(pix_fmt_str, &tail, 10);
 -        if (*tail || c->pix_fmt < 0 || c->pix_fmt >= PIX_FMT_NB) {
 -            av_log(ctx, AV_LOG_ERROR, "Invalid pixel format string '%s'\n", pix_fmt_str);
 -            return AVERROR(EINVAL);
 +    colon = strchr(args, ':');
 +    equal = strchr(args, '=');
 +    if (equal && (!colon || equal < colon)) {
 +        av_opt_set_defaults(c);
 +        ret = av_set_options_string(c, args, "=", ":");
 +        if (ret < 0) {
 +            av_log(ctx, AV_LOG_ERROR, "Error parsing options string: %s\n", args);
 +            goto fail;
          }
 +    } else {
 +    if ((n = sscanf(args, "%d:%d:%127[^:]:%d:%d:%d:%d:%255c", &c->w, &c->h, pix_fmt_str,
 +                    &c->time_base.num, &c->time_base.den,
 +                    &c->pixel_aspect.num, &c->pixel_aspect.den, sws_param)) < 7) {
 +        av_log(ctx, AV_LOG_ERROR, "Expected at least 7 arguments, but only %d found in '%s'\n", n, args);
 +        ret = AVERROR(EINVAL);
 +        goto fail;
      }
 +    av_log(ctx, AV_LOG_WARNING, "Flat options syntax is deprecated, use key=value pairs\n");
  
 -    if (!(c->fifo = av_fifo_alloc(sizeof(AVFilterBufferRef*))))
 -        return AVERROR(ENOMEM);
 +    if ((ret = ff_parse_pixel_format(&c->pix_fmt, pix_fmt_str, ctx)) < 0)
 +        goto fail;
 +    c->sws_param = av_strdup(sws_param);
 +    if (!c->sws_param) {
 +        ret = AVERROR(ENOMEM);
 +        goto fail;
 +    }
 +    }
 +
 +    if (!(c->fifo = av_fifo_alloc(sizeof(AVFilterBufferRef*)))) {
 +        ret = AVERROR(ENOMEM);
 +        goto fail;
 +    }
  
 -    av_log(ctx, AV_LOG_INFO, "w:%d h:%d pixfmt:%s\n", c->w, c->h, av_pix_fmt_descriptors[c->pix_fmt].name);
 +    av_log(ctx, AV_LOG_INFO, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d sws_param:%s\n",
 +           c->w, c->h, av_pix_fmt_descriptors[c->pix_fmt].name,
 +           c->time_base.num, c->time_base.den, c->frame_rate.num, c->frame_rate.den,
 +           c->pixel_aspect.num, c->pixel_aspect.den, (char *)av_x_if_null(c->sws_param, ""));
      return 0;
 +
 +fail:
 +    av_opt_free(c);
 +    return ret;
  }
  
 -#define OFFSET(x) offsetof(BufferSourceContext, x)
  #define A AV_OPT_FLAG_AUDIO_PARAM
 -static const AVOption audio_options[] = {
 +static const AVOption abuffer_options[] = {
      { "time_base",      NULL, OFFSET(time_base),           AV_OPT_TYPE_RATIONAL, { 0 }, 0, INT_MAX, A },
      { "sample_rate",    NULL, OFFSET(sample_rate),         AV_OPT_TYPE_INT,      { 0 }, 0, INT_MAX, A },
      { "sample_fmt",     NULL, OFFSET(sample_fmt_str),      AV_OPT_TYPE_STRING,             .flags = A },
      { NULL },
  };
  
 -static const AVClass abuffer_class = {
 -    .class_name = "abuffer source",
 -    .item_name  = av_default_item_name,
 -    .option     = audio_options,
 -    .version    = LIBAVUTIL_VERSION_INT,
 -};
 +AVFILTER_DEFINE_CLASS(abuffer);
  
- static av_cold int init_audio(AVFilterContext *ctx, const char *args, void *opaque)
+ static av_cold int init_audio(AVFilterContext *ctx, const char *args)
  {
      BufferSourceContext *s = ctx->priv;
      int ret = 0;
Simple merge
index c4f6118,0000000..85fbee2
mode 100644,000000..100644
--- /dev/null
@@@ -1,301 -1,0 +1,307 @@@
- static av_cold int vsink_init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * buffer video sink
 + */
 +
 +#include "libavutil/avassert.h"
 +#include "libavutil/fifo.h"
 +#include "avfilter.h"
 +#include "buffersink.h"
 +#include "internal.h"
 +
 +AVBufferSinkParams *av_buffersink_params_alloc(void)
 +{
 +    static const int pixel_fmts[] = { -1 };
 +    AVBufferSinkParams *params = av_malloc(sizeof(AVBufferSinkParams));
 +    if (!params)
 +        return NULL;
 +
 +    params->pixel_fmts = pixel_fmts;
 +    return params;
 +}
 +
 +AVABufferSinkParams *av_abuffersink_params_alloc(void)
 +{
 +    static const int sample_fmts[] = { -1 };
 +    static const int64_t channel_layouts[] = { -1 };
 +    AVABufferSinkParams *params = av_malloc(sizeof(AVABufferSinkParams));
 +
 +    if (!params)
 +        return NULL;
 +
 +    params->sample_fmts = sample_fmts;
 +    params->channel_layouts = channel_layouts;
 +    return params;
 +}
 +
 +typedef struct {
 +    AVFifoBuffer *fifo;                      ///< FIFO buffer of video frame references
 +
 +    /* only used for video */
 +    enum PixelFormat *pixel_fmts;           ///< list of accepted pixel formats, must be terminated with -1
 +
 +    /* only used for audio */
 +    enum AVSampleFormat *sample_fmts;       ///< list of accepted sample formats, terminated by AV_SAMPLE_FMT_NONE
 +    int64_t *channel_layouts;               ///< list of accepted channel layouts, terminated by -1
 +} BufferSinkContext;
 +
 +#define FIFO_INIT_SIZE 8
 +
 +static av_cold int common_init(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +
 +    buf->fifo = av_fifo_alloc(FIFO_INIT_SIZE*sizeof(AVFilterBufferRef *));
 +    if (!buf->fifo) {
 +        av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo\n");
 +        return AVERROR(ENOMEM);
 +    }
 +    return 0;
 +}
 +
 +static av_cold void common_uninit(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +    AVFilterBufferRef *picref;
 +
 +    if (buf->fifo) {
 +        while (av_fifo_size(buf->fifo) >= sizeof(AVFilterBufferRef *)) {
 +            av_fifo_generic_read(buf->fifo, &picref, sizeof(picref), NULL);
 +            avfilter_unref_buffer(picref);
 +        }
 +        av_fifo_free(buf->fifo);
 +        buf->fifo = NULL;
 +    }
 +}
 +
 +static void end_frame(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    BufferSinkContext *buf = inlink->dst->priv;
 +
 +    av_assert1(inlink->cur_buf);
 +    if (av_fifo_space(buf->fifo) < sizeof(AVFilterBufferRef *)) {
 +        /* realloc fifo size */
 +        if (av_fifo_realloc2(buf->fifo, av_fifo_size(buf->fifo) * 2) < 0) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Cannot buffer more frames. Consume some available frames "
 +                   "before adding new ones.\n");
 +            return;
 +        }
 +    }
 +
 +    /* cache frame */
 +    av_fifo_generic_write(buf->fifo,
 +                          &inlink->cur_buf, sizeof(AVFilterBufferRef *), NULL);
 +}
 +
 +int av_buffersink_get_buffer_ref(AVFilterContext *ctx,
 +                                  AVFilterBufferRef **bufref, int flags)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    int ret;
 +    *bufref = NULL;
 +
 +    av_assert0(!strcmp(ctx->filter->name, "buffersink") || !strcmp(ctx->filter->name, "abuffersink"));
 +
 +    /* no picref available, fetch it from the filterchain */
 +    if (!av_fifo_size(buf->fifo)) {
 +        if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST)
 +            return AVERROR(EAGAIN);
 +        if ((ret = ff_request_frame(inlink)) < 0)
 +            return ret;
 +    }
 +
 +    if (!av_fifo_size(buf->fifo))
 +        return AVERROR(EINVAL);
 +
 +    if (flags & AV_BUFFERSINK_FLAG_PEEK)
 +        *bufref = *((AVFilterBufferRef **)av_fifo_peek2(buf->fifo, 0));
 +    else
 +        av_fifo_generic_read(buf->fifo, bufref, sizeof(*bufref), NULL);
 +
 +    return 0;
 +}
 +
 +AVRational av_buffersink_get_frame_rate(AVFilterContext *ctx)
 +{
 +    av_assert0(!strcmp(ctx->filter->name, "buffersink"));
 +
 +    return ctx->inputs[0]->frame_rate;
 +}
 +
 +int av_buffersink_poll_frame(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +
 +    av_assert0(!strcmp(ctx->filter->name, "buffersink") || !strcmp(ctx->filter->name, "abuffersink"));
 +
 +    return av_fifo_size(buf->fifo)/sizeof(AVFilterBufferRef *) + ff_poll_frame(inlink);
 +}
 +
 +#if CONFIG_BUFFERSINK_FILTER
 +
-     AVBufferSinkParams *params = (AVBufferSinkParams *)opaque;
++static av_cold int vsink_init(AVFilterContext *ctx, const char *args)
 +{
 +    BufferSinkContext *buf = ctx->priv;
-     if (!opaque) {
++    AVBufferSinkParams *params = NULL;
 +
- static av_cold int asink_init(AVFilterContext *ctx, const char *args, void *opaque)
++//     if(args && !strcmp(args, "opaque"))
++//         params = (AVBufferSinkParams *)(args+7);
++
++    if (!params) {
 +        av_log(ctx, AV_LOG_WARNING,
 +               "No opaque field provided\n");
 +        buf->pixel_fmts = NULL;
 +    } else {
 +        const int *pixel_fmts = params->pixel_fmts;
 +
 +        buf->pixel_fmts = ff_copy_int_list(pixel_fmts);
 +        if (!buf->pixel_fmts)
 +            return AVERROR(ENOMEM);
 +    }
 +
 +    return common_init(ctx);
 +}
 +
 +static av_cold void vsink_uninit(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +    av_freep(&buf->pixel_fmts);
 +    common_uninit(ctx);
 +}
 +
 +static int vsink_query_formats(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +
 +    if (buf->pixel_fmts)
 +        ff_set_common_formats(ctx, ff_make_format_list(buf->pixel_fmts));
 +    else
 +        ff_default_query_formats(ctx);
 +
 +    return 0;
 +}
 +
 +AVFilter avfilter_vsink_buffersink = {
 +    .name      = "buffersink",
 +    .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."),
 +    .priv_size = sizeof(BufferSinkContext),
 +    .init      = vsink_init,
 +    .uninit    = vsink_uninit,
 +
 +    .query_formats = vsink_query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name    = "default",
 +                                    .type          = AVMEDIA_TYPE_VIDEO,
 +                                    .end_frame     = end_frame,
 +                                    .min_perms     = AV_PERM_READ, },
 +                                  { .name = NULL }},
 +    .outputs   = (const AVFilterPad[]) {{ .name = NULL }},
 +};
 +
 +#endif /* CONFIG_BUFFERSINK_FILTER */
 +
 +#if CONFIG_ABUFFERSINK_FILTER
 +
 +static void filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
 +{
 +    end_frame(link);
 +}
 +
-     AVABufferSinkParams *params = opaque;
++static av_cold int asink_init(AVFilterContext *ctx, const char *args)
 +{
 +    BufferSinkContext *buf = ctx->priv;
++    AVABufferSinkParams *params = NULL;
++
++//     if(args && !strcmp(args, "opaque"))
++//         params = (AVABufferSinkParams *)(args+7);
 +
 +    if (params && params->sample_fmts) {
 +        buf->sample_fmts     = ff_copy_int_list  (params->sample_fmts);
 +        if (!buf->sample_fmts)
 +            goto fail_enomem;
 +    }
 +    if (params && params->channel_layouts) {
 +        buf->channel_layouts = ff_copy_int64_list(params->channel_layouts);
 +        if (!buf->channel_layouts)
 +            goto fail_enomem;
 +    }
 +    if (!common_init(ctx))
 +        return 0;
 +
 +fail_enomem:
 +    av_freep(&buf->sample_fmts);
 +    av_freep(&buf->channel_layouts);
 +    return AVERROR(ENOMEM);
 +}
 +
 +static av_cold void asink_uninit(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +
 +    av_freep(&buf->sample_fmts);
 +    av_freep(&buf->channel_layouts);
 +    common_uninit(ctx);
 +}
 +
 +static int asink_query_formats(AVFilterContext *ctx)
 +{
 +    BufferSinkContext *buf = ctx->priv;
 +    AVFilterFormats *formats = NULL;
 +    AVFilterChannelLayouts *layouts = NULL;
 +
 +    if (buf->sample_fmts) {
 +    if (!(formats = ff_make_format_list(buf->sample_fmts)))
 +        return AVERROR(ENOMEM);
 +    ff_set_common_formats(ctx, formats);
 +    }
 +
 +    if (buf->channel_layouts) {
 +    if (!(layouts = avfilter_make_format64_list(buf->channel_layouts)))
 +        return AVERROR(ENOMEM);
 +    ff_set_common_channel_layouts(ctx, layouts);
 +    }
 +
 +    return 0;
 +}
 +
 +AVFilter avfilter_asink_abuffersink = {
 +    .name      = "abuffersink",
 +    .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."),
 +    .init      = asink_init,
 +    .uninit    = asink_uninit,
 +    .priv_size = sizeof(BufferSinkContext),
 +    .query_formats = asink_query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name     = "default",
 +                                    .type           = AVMEDIA_TYPE_AUDIO,
 +                                    .filter_samples = filter_samples,
 +                                    .min_perms      = AV_PERM_READ, },
 +                                  { .name = NULL }},
 +    .outputs   = (const AVFilterPad[]) {{ .name = NULL }},
 +};
 +
 +#endif /* CONFIG_ABUFFERSINK_FILTER */
Simple merge
@@@ -80,10 -69,18 +80,10 @@@ static const AVOption movie_options[]= 
  {NULL},
  };
  
 -static const char *movie_get_name(void *ctx)
 -{
 -    return "movie";
 -}
 -
 -static const AVClass movie_class = {
 -    "MovieContext",
 -    movie_get_name,
 -    movie_options
 -};
 +AVFILTER_DEFINE_CLASS(movie);
  
- static av_cold int movie_common_init(AVFilterContext *ctx, const char *args, void *opaque,
 -static int movie_init(AVFilterContext *ctx)
++static av_cold int movie_common_init(AVFilterContext *ctx, const char *args,
 +                                     enum AVMediaType type)
  {
      MovieContext *movie = ctx->priv;
      AVInputFormat *iformat = NULL;
@@@ -189,30 -196,11 +189,30 @@@ static av_cold void movie_common_uninit
          avcodec_close(movie->codec_ctx);
      if (movie->format_ctx)
          avformat_close_input(&movie->format_ctx);
 +
      avfilter_unref_buffer(movie->picref);
      av_freep(&movie->frame);
- static av_cold int movie_init(AVFilterContext *ctx, const char *args, void *opaque)
 +
 +    avfilter_unref_buffer(movie->samplesref);
 +}
 +
 +#if CONFIG_MOVIE_FILTER
 +
-     if ((ret = movie_common_init(ctx, args, opaque, AVMEDIA_TYPE_VIDEO)) < 0)
++static av_cold int movie_init(AVFilterContext *ctx, const char *args)
 +{
 +    MovieContext *movie = ctx->priv;
 +    int ret;
 +
++    if ((ret = movie_common_init(ctx, args, AVMEDIA_TYPE_VIDEO)) < 0)
 +        return ret;
 +
 +    movie->w = movie->codec_ctx->width;
 +    movie->h = movie->codec_ctx->height;
 +
 +    return 0;
  }
  
 -static int query_formats(AVFilterContext *ctx)
 +static int movie_query_formats(AVFilterContext *ctx)
  {
      MovieContext *movie = ctx->priv;
      enum PixelFormat pix_fmts[] = { movie->codec_ctx->pix_fmt, PIX_FMT_NONE };
@@@ -329,156 -302,14 +329,156 @@@ AVFilter avfilter_vsrc_movie = 
      .name          = "movie",
      .description   = NULL_IF_CONFIG_SMALL("Read from a movie source."),
      .priv_size     = sizeof(MovieContext),
 -    .init          = init,
 -    .uninit        = uninit,
 -    .query_formats = query_formats,
 +    .init          = movie_init,
 +    .uninit        = movie_common_uninit,
 +    .query_formats = movie_query_formats,
  
 -    .inputs    = (AVFilterPad[]) {{ .name = NULL }},
 -    .outputs   = (AVFilterPad[]) {{ .name            = "default",
 +    .inputs    = (const AVFilterPad[]) {{ .name = NULL }},
 +    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
                                      .type            = AVMEDIA_TYPE_VIDEO,
 -                                    .request_frame   = request_frame,
 -                                    .config_props    = config_output_props, },
 +                                    .request_frame   = movie_request_frame,
 +                                    .config_props    = movie_config_output_props, },
 +                                  { .name = NULL}},
 +};
 +
 +#endif  /* CONFIG_MOVIE_FILTER */
 +
 +#if CONFIG_AMOVIE_FILTER
 +
- static av_cold int amovie_init(AVFilterContext *ctx, const char *args, void *opaque)
++static av_cold int amovie_init(AVFilterContext *ctx, const char *args)
 +{
 +    MovieContext *movie = ctx->priv;
 +    int ret;
 +
-     if ((ret = movie_common_init(ctx, args, opaque, AVMEDIA_TYPE_AUDIO)) < 0)
++    if ((ret = movie_common_init(ctx, args, AVMEDIA_TYPE_AUDIO)) < 0)
 +        return ret;
 +
 +    movie->bps = av_get_bytes_per_sample(movie->codec_ctx->sample_fmt);
 +    return 0;
 +}
 +
 +static int amovie_query_formats(AVFilterContext *ctx)
 +{
 +    MovieContext *movie = ctx->priv;
 +    AVCodecContext *c = movie->codec_ctx;
 +
 +    enum AVSampleFormat sample_fmts[] = { c->sample_fmt, -1 };
 +    int sample_rates[] = { c->sample_rate, -1 };
 +    int64_t chlayouts[] = { c->channel_layout ? c->channel_layout :
 +                            av_get_default_channel_layout(c->channels), -1 };
 +
 +    ff_set_common_formats        (ctx, ff_make_format_list(sample_fmts));
 +    ff_set_common_samplerates    (ctx, ff_make_format_list(sample_rates));
 +    ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts));
 +
 +    return 0;
 +}
 +
 +static int amovie_config_output_props(AVFilterLink *outlink)
 +{
 +    MovieContext *movie = outlink->src->priv;
 +    AVCodecContext *c = movie->codec_ctx;
 +
 +    outlink->sample_rate = c->sample_rate;
 +    outlink->time_base = movie->format_ctx->streams[movie->stream_index]->time_base;
 +
 +    return 0;
 +}
 +
 +static int amovie_get_samples(AVFilterLink *outlink)
 +{
 +    MovieContext *movie = outlink->src->priv;
 +    AVPacket pkt;
 +    int ret, got_frame = 0;
 +
 +    if (!movie->pkt.size && movie->is_done == 1)
 +        return AVERROR_EOF;
 +
 +    /* check for another frame, in case the previous one was completely consumed */
 +    if (!movie->pkt.size) {
 +        while ((ret = av_read_frame(movie->format_ctx, &pkt)) >= 0) {
 +            // Is this a packet from the selected stream?
 +            if (pkt.stream_index != movie->stream_index) {
 +                av_free_packet(&pkt);
 +                continue;
 +            } else {
 +                movie->pkt0 = movie->pkt = pkt;
 +                break;
 +            }
 +        }
 +
 +        if (ret == AVERROR_EOF) {
 +            movie->is_done = 1;
 +            return ret;
 +        }
 +    }
 +
 +    /* decode and update the movie pkt */
 +    avcodec_get_frame_defaults(movie->frame);
 +    ret = avcodec_decode_audio4(movie->codec_ctx, movie->frame, &got_frame, &movie->pkt);
 +    if (ret < 0) {
 +        movie->pkt.size = 0;
 +        return ret;
 +    }
 +    movie->pkt.data += ret;
 +    movie->pkt.size -= ret;
 +
 +    /* wrap the decoded data in a samplesref */
 +    if (got_frame) {
 +        int nb_samples = movie->frame->nb_samples;
 +        int data_size =
 +            av_samples_get_buffer_size(NULL, movie->codec_ctx->channels,
 +                                       nb_samples, movie->codec_ctx->sample_fmt, 1);
 +        if (data_size < 0)
 +            return data_size;
 +        movie->samplesref =
 +            ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples);
 +        memcpy(movie->samplesref->data[0], movie->frame->data[0], data_size);
 +        movie->samplesref->pts = movie->pkt.pts;
 +        movie->samplesref->pos = movie->pkt.pos;
 +        movie->samplesref->audio->sample_rate = movie->codec_ctx->sample_rate;
 +    }
 +
 +    // We got it. Free the packet since we are returning
 +    if (movie->pkt.size <= 0)
 +        av_free_packet(&movie->pkt0);
 +
 +    return 0;
 +}
 +
 +static int amovie_request_frame(AVFilterLink *outlink)
 +{
 +    MovieContext *movie = outlink->src->priv;
 +    int ret;
 +
 +    if (movie->is_done)
 +        return AVERROR_EOF;
 +    do {
 +        if ((ret = amovie_get_samples(outlink)) < 0)
 +            return ret;
 +    } while (!movie->samplesref);
 +
 +    ff_filter_samples(outlink, avfilter_ref_buffer(movie->samplesref, ~0));
 +    avfilter_unref_buffer(movie->samplesref);
 +    movie->samplesref = NULL;
 +
 +    return 0;
 +}
 +
 +AVFilter avfilter_asrc_amovie = {
 +    .name          = "amovie",
 +    .description   = NULL_IF_CONFIG_SMALL("Read audio from a movie source."),
 +    .priv_size     = sizeof(MovieContext),
 +    .init          = amovie_init,
 +    .uninit        = movie_common_uninit,
 +    .query_formats = amovie_query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name = NULL }},
 +    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .type            = AVMEDIA_TYPE_AUDIO,
 +                                    .request_frame   = amovie_request_frame,
 +                                    .config_props    = amovie_config_output_props, },
                                    { .name = NULL}},
  };
 +
 +#endif /* CONFIG_AMOVIE_FILTER */
  #include "video.h"
  
  typedef struct {
 -    AVRational aspect;
 +    AVRational ratio;
  } AspectContext;
  
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
+ static av_cold int init(AVFilterContext *ctx, const char *args)
  {
      AspectContext *aspect = ctx->priv;
 -    double  ratio;
 -    int64_t gcd;
 -    char c = 0;
 +    aspect->ratio = (AVRational) {0, 1};
  
      if (args) {
 -        if (sscanf(args, "%d:%d%c", &aspect->aspect.num, &aspect->aspect.den, &c) != 2)
 -            if (sscanf(args, "%lf%c", &ratio, &c) == 1)
 -                aspect->aspect = av_d2q(ratio, 100);
 -
 -        if (c || aspect->aspect.num <= 0 || aspect->aspect.den <= 0) {
 +        if (av_parse_ratio(&aspect->ratio, args, 100, 0, ctx) < 0 ||
 +            aspect->ratio.num < 0 || aspect->ratio.den <= 0) {
              av_log(ctx, AV_LOG_ERROR,
                     "Invalid string '%s' for aspect ratio.\n", args);
              return AVERROR(EINVAL);
index 7c34c48,0000000..81dc773
mode 100644,000000..100644
--- /dev/null
@@@ -1,228 -1,0 +1,228 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Baptiste Coudurier
 + * Copyright (c) 2011 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Libass subtitles burning filter.
 + *
 + * @see{http://www.matroska.org/technical/specs/subtitles/ssa.html}
 + */
 +
 +#include <ass/ass.h>
 +
 +#include "libavutil/avstring.h"
 +#include "libavutil/imgutils.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/parseutils.h"
 +#include "drawutils.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +#include "formats.h"
 +#include "video.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    ASS_Library  *library;
 +    ASS_Renderer *renderer;
 +    ASS_Track    *track;
 +    char *filename;
 +    uint8_t rgba_map[4];
 +    int     pix_step[4];       ///< steps per pixel for each plane of the main output
 +    int original_w, original_h;
 +    FFDrawContext draw;
 +} AssContext;
 +
 +#define OFFSET(x) offsetof(AssContext, x)
 +
 +static const AVOption ass_options[] = {
 +    {"original_size",  "set the size of the original video (used to scale fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL},  CHAR_MIN, CHAR_MAX },
 +    {NULL},
 +};
 +
 +AVFILTER_DEFINE_CLASS(ass);
 +
 +/* libass supports a log level ranging from 0 to 7 */
 +int ass_libav_log_level_map[] = {
 +    AV_LOG_QUIET,               /* 0 */
 +    AV_LOG_PANIC,               /* 1 */
 +    AV_LOG_FATAL,               /* 2 */
 +    AV_LOG_ERROR,               /* 3 */
 +    AV_LOG_WARNING,             /* 4 */
 +    AV_LOG_INFO,                /* 5 */
 +    AV_LOG_VERBOSE,             /* 6 */
 +    AV_LOG_DEBUG,               /* 7 */
 +};
 +
 +static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx)
 +{
 +    int level = ass_libav_log_level_map[ass_level];
 +
 +    av_vlog(ctx, level, fmt, args);
 +    av_log(ctx, level, "\n");
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    AssContext *ass = ctx->priv;
 +    int ret;
 +
 +    ass->class = &ass_class;
 +    av_opt_set_defaults(ass);
 +
 +    if (args)
 +        ass->filename = av_get_token(&args, ":");
 +    if (!ass->filename || !*ass->filename) {
 +        av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (*args++ == ':' && (ret = av_set_options_string(ass, args, "=", ":")) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
 +        return ret;
 +    }
 +
 +    ass->library = ass_library_init();
 +    if (!ass->library) {
 +        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
 +        return AVERROR(EINVAL);
 +    }
 +    ass_set_message_cb(ass->library, ass_log, ctx);
 +
 +    ass->renderer = ass_renderer_init(ass->library);
 +    if (!ass->renderer) {
 +        av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    ass->track = ass_read_file(ass->library, ass->filename, NULL);
 +    if (!ass->track) {
 +        av_log(ctx, AV_LOG_ERROR,
 +               "Could not create a libass track when reading file '%s'\n",
 +               ass->filename);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1);
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    AssContext *ass = ctx->priv;
 +
 +    av_freep(&ass->filename);
 +    if (ass->track)
 +        ass_free_track(ass->track);
 +    if (ass->renderer)
 +        ass_renderer_done(ass->renderer);
 +    if (ass->library)
 +        ass_library_done(ass->library);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
 +    return 0;
 +}
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AssContext *ass = inlink->dst->priv;
 +
 +    ff_draw_init(&ass->draw, inlink->format, 0);
 +
 +    ass_set_frame_size  (ass->renderer, inlink->w, inlink->h);
 +    if (ass->original_w && ass->original_h)
 +        ass_set_aspect_ratio(ass->renderer, (double)inlink->w / inlink->h,
 +                             (double)ass->original_w / ass->original_h);
 +
 +    return 0;
 +}
 +
 +static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
 +
 +/* libass stores an RGBA color in the format RRGGBBTT, where TT is the transparency level */
 +#define AR(c)  ( (c)>>24)
 +#define AG(c)  (((c)>>16)&0xFF)
 +#define AB(c)  (((c)>>8) &0xFF)
 +#define AA(c)  ((0xFF-c) &0xFF)
 +
 +static void overlay_ass_image(AssContext *ass, AVFilterBufferRef *picref,
 +                              const ASS_Image *image)
 +{
 +    for (; image; image = image->next) {
 +        uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)};
 +        FFDrawColor color;
 +        ff_draw_color(&ass->draw, &color, rgba_color);
 +        ff_blend_mask(&ass->draw, &color,
 +                      picref->data, picref->linesize,
 +                      picref->video->w, picref->video->h,
 +                      image->bitmap, image->stride, image->w, image->h,
 +                      3, 0, image->dst_x, image->dst_y);
 +    }
 +}
 +
 +static void end_frame(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    AVFilterLink *outlink = ctx->outputs[0];
 +    AssContext *ass = ctx->priv;
 +    AVFilterBufferRef *picref = inlink->cur_buf;
 +    int detect_change = 0;
 +    double time_ms = picref->pts * av_q2d(inlink->time_base) * 1000;
 +    ASS_Image *image = ass_render_frame(ass->renderer, ass->track,
 +                                        time_ms, &detect_change);
 +
 +    if (detect_change)
 +        av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%f\n", time_ms);
 +
 +    overlay_ass_image(ass, picref, image);
 +
 +    ff_draw_slice(outlink, 0, picref->video->h, 1);
 +    ff_end_frame(outlink);
 +}
 +
 +AVFilter avfilter_vf_ass = {
 +    .name          = "ass",
 +    .description   = NULL_IF_CONFIG_SMALL("Render subtitles onto input video using the libass library."),
 +    .priv_size     = sizeof(AssContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +
 +    .inputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_VIDEO,
 +          .get_video_buffer = ff_null_get_video_buffer,
 +          .start_frame      = ff_null_start_frame,
 +          .draw_slice       = null_draw_slice,
 +          .end_frame        = end_frame,
 +          .config_props     = config_input,
 +          .min_perms        = AV_PERM_WRITE | AV_PERM_READ,
 +          .rej_perms        = AV_PERM_PRESERVE },
 +        { .name = NULL}
 +    },
 +    .outputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_VIDEO, },
 +        { .name = NULL}
 +    },
 +};
index 6fc6935,0000000..99066a6
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,115 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2012 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * bounding box detection filter
 + */
 +
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/timestamp.h"
 +#include "avfilter.h"
 +#include "bbox.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    unsigned int frame;
 +    int vsub, hsub;
 +} BBoxContext;
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    BBoxContext *bbox = ctx->priv;
 +    bbox->frame = 0;
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    static const enum PixelFormat pix_fmts[] = {
 +        PIX_FMT_YUV420P,
 +        PIX_FMT_YUV444P,
 +        PIX_FMT_YUV440P,
 +        PIX_FMT_YUV422P,
 +        PIX_FMT_YUV411P,
 +        PIX_FMT_NONE,
 +    };
 +
 +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
 +    return 0;
 +}
 +
 +static void end_frame(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    BBoxContext *bbox = ctx->priv;
 +    AVFilterBufferRef *picref = inlink->cur_buf;
 +    FFBoundingBox box;
 +    int has_bbox, w, h;
 +
 +    has_bbox =
 +        ff_calculate_bounding_box(&box,
 +                                  picref->data[0], picref->linesize[0],
 +                                  inlink->w, inlink->h, 16);
 +    w = box.x2 - box.x1 + 1;
 +    h = box.y2 - box.y1 + 1;
 +
 +    av_log(ctx, AV_LOG_INFO,
 +           "n:%d pts:%s pts_time:%s", bbox->frame,
 +           av_ts2str(picref->pts), av_ts2timestr(picref->pts, &inlink->time_base));
 +
 +    if (has_bbox) {
 +        av_log(ctx, AV_LOG_INFO,
 +               " x1:%d x2:%d y1:%d y2:%d w:%d h:%d"
 +               " crop=%d:%d:%d:%d drawbox=%d:%d:%d:%d",
 +               box.x1, box.x2, box.y1, box.y2, w, h,
 +               w, h, box.x1, box.y1,    /* crop params */
 +               box.x1, box.y1, w, h);   /* drawbox params */
 +    }
 +    av_log(ctx, AV_LOG_INFO, "\n");
 +
 +    bbox->frame++;
 +    avfilter_unref_buffer(picref);
 +    ff_end_frame(inlink->dst->outputs[0]);
 +}
 +
 +AVFilter avfilter_vf_bbox = {
 +    .name          = "bbox",
 +    .description   = NULL_IF_CONFIG_SMALL("Compute bounding box for each frame."),
 +    .priv_size     = sizeof(BBoxContext),
 +    .query_formats = query_formats,
 +    .init          = init,
 +
 +    .inputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_VIDEO,
 +          .get_video_buffer = ff_null_get_video_buffer,
 +          .start_frame      = ff_null_start_frame_keep_ref,
 +          .end_frame        = end_frame,
 +          .min_perms        = AV_PERM_READ, },
 +        { .name = NULL }
 +    },
 +
 +    .outputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_VIDEO },
 +        { .name = NULL }
 +    },
 +};
index 9d29c45,0000000..bb9567d
mode 100644,000000..100644
--- /dev/null
@@@ -1,225 -1,0 +1,225 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2012 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Video black detector, loosely based on blackframe with extended
 + * syntax and features
 + */
 +
 +#include <float.h>
 +#include "libavutil/opt.h"
 +#include "libavutil/timestamp.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +typedef struct {
 +    const AVClass *class;
 +    double  black_min_duration_time; ///< minimum duration of detected black, in seconds
 +    int64_t black_min_duration;      ///< minimum duration of detected black, expressed in timebase units
 +    int64_t black_start;             ///< pts start time of the first black picture
 +    int64_t black_end;               ///< pts end time of the last black picture
 +    int64_t last_picref_pts;         ///< pts of the last input picture
 +    int black_started;
 +
 +    double       picture_black_ratio_th;
 +    double       pixel_black_th;
 +    unsigned int pixel_black_th_i;
 +
 +    unsigned int frame_count;       ///< frame number
 +    unsigned int nb_black_pixels;   ///< number of black pixels counted so far
 +} BlackDetectContext;
 +
 +#define OFFSET(x) offsetof(BlackDetectContext, x)
 +static const AVOption blackdetect_options[] = {
 +    { "d",                  "set minimum detected black duration in seconds", OFFSET(black_min_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, DBL_MAX},
 +    { "black_min_duration", "set minimum detected black duration in seconds", OFFSET(black_min_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, DBL_MAX},
 +    { "picture_black_ratio_th", "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1},
 +    { "pic_th",                 "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1},
 +    { "pixel_black_th", "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1},
 +    { "pix_th",         "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1},
 +    { NULL },
 +};
 +
 +AVFILTER_DEFINE_CLASS(blackdetect);
 +
 +#define YUVJ_FORMATS \
 +    PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
 +
 +static enum PixelFormat yuvj_formats[] = {
 +    YUVJ_FORMATS, PIX_FMT_NONE
 +};
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    static const enum PixelFormat pix_fmts[] = {
 +        PIX_FMT_YUV410P, PIX_FMT_YUV420P, PIX_FMT_GRAY8, PIX_FMT_NV12,
 +        PIX_FMT_NV21, PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV411P,
 +        YUVJ_FORMATS,
 +        PIX_FMT_NONE
 +    };
 +
 +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
 +    return 0;
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    int ret;
 +    BlackDetectContext *blackdetect = ctx->priv;
 +
 +    blackdetect->class = &blackdetect_class;
 +    av_opt_set_defaults(blackdetect);
 +
 +    if ((ret = av_set_options_string(blackdetect, args, "=", ":")) < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
 +        return ret;
 +    }
 +
 +    return 0;
 +}
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    BlackDetectContext *blackdetect = ctx->priv;
 +
 +    blackdetect->black_min_duration =
 +        blackdetect->black_min_duration_time / av_q2d(inlink->time_base);
 +
 +    blackdetect->pixel_black_th_i = ff_fmt_is_in(inlink->format, yuvj_formats) ?
 +        // luminance_minimum_value + pixel_black_th * luminance_range_size
 +             blackdetect->pixel_black_th *  255 :
 +        16 + blackdetect->pixel_black_th * (235 - 16);
 +
 +    av_log(blackdetect, AV_LOG_INFO,
 +           "black_min_duration:%s pixel_black_th:%f pixel_black_th_i:%d picture_black_ratio_th:%f\n",
 +           av_ts2timestr(blackdetect->black_min_duration, &inlink->time_base),
 +           blackdetect->pixel_black_th, blackdetect->pixel_black_th_i,
 +           blackdetect->picture_black_ratio_th);
 +    return 0;
 +}
 +
 +static void check_black_end(AVFilterContext *ctx)
 +{
 +    BlackDetectContext *blackdetect = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +
 +    if ((blackdetect->black_end - blackdetect->black_start) >= blackdetect->black_min_duration) {
 +        av_log(blackdetect, AV_LOG_INFO,
 +               "black_start:%s black_end:%s black_duration:%s\n",
 +               av_ts2timestr(blackdetect->black_start, &inlink->time_base),
 +               av_ts2timestr(blackdetect->black_end,   &inlink->time_base),
 +               av_ts2timestr(blackdetect->black_end - blackdetect->black_start, &inlink->time_base));
 +    }
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    AVFilterContext *ctx = outlink->src;
 +    BlackDetectContext *blackdetect = ctx->priv;
 +    AVFilterLink *inlink = ctx->inputs[0];
 +    int ret = ff_request_frame(inlink);
 +
 +    if (ret == AVERROR_EOF && blackdetect->black_started) {
 +        // FIXME: black_end should be set to last_picref_pts + last_picref_duration
 +        blackdetect->black_end = blackdetect->last_picref_pts;
 +        check_black_end(ctx);
 +    }
 +    return ret;
 +}
 +
 +static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    BlackDetectContext *blackdetect = ctx->priv;
 +    AVFilterBufferRef *picref = inlink->cur_buf;
 +    int x, i;
 +    const uint8_t *p = picref->data[0] + y * picref->linesize[0];
 +
 +    for (i = 0; i < h; i++) {
 +        for (x = 0; x < inlink->w; x++)
 +            blackdetect->nb_black_pixels += p[x] <= blackdetect->pixel_black_th_i;
 +        p += picref->linesize[0];
 +    }
 +
 +    ff_draw_slice(ctx->outputs[0], y, h, slice_dir);
 +}
 +
 +static void end_frame(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    BlackDetectContext *blackdetect = ctx->priv;
 +    AVFilterBufferRef *picref = inlink->cur_buf;
 +    double picture_black_ratio = 0;
 +
 +    picture_black_ratio = (double)blackdetect->nb_black_pixels / (inlink->w * inlink->h);
 +
 +    av_log(ctx, AV_LOG_DEBUG,
 +           "frame:%u picture_black_ratio:%f pos:%"PRId64" pts:%s t:%s type:%c\n",
 +           blackdetect->frame_count, picture_black_ratio,
 +           picref->pos, av_ts2str(picref->pts), av_ts2timestr(picref->pts, &inlink->time_base),
 +           av_get_picture_type_char(picref->video->pict_type));
 +
 +    if (picture_black_ratio >= blackdetect->picture_black_ratio_th) {
 +        if (!blackdetect->black_started) {
 +            /* black starts here */
 +            blackdetect->black_started = 1;
 +            blackdetect->black_start = picref->pts;
 +        }
 +    } else if (blackdetect->black_started) {
 +        /* black ends here */
 +        blackdetect->black_started = 0;
 +        blackdetect->black_end = picref->pts;
 +        check_black_end(ctx);
 +    }
 +
 +    blackdetect->last_picref_pts = picref->pts;
 +    blackdetect->frame_count++;
 +    blackdetect->nb_black_pixels = 0;
 +    avfilter_unref_buffer(picref);
 +    ff_end_frame(inlink->dst->outputs[0]);
 +}
 +
 +AVFilter avfilter_vf_blackdetect = {
 +    .name          = "blackdetect",
 +    .description   = NULL_IF_CONFIG_SMALL("Detect video intervals that are (almost) black."),
 +    .priv_size     = sizeof(BlackDetectContext),
 +    .init          = init,
 +    .query_formats = query_formats,
 +
 +    .inputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_VIDEO,
 +          .config_props     = config_input,
 +          .draw_slice       = draw_slice,
 +          .get_video_buffer = ff_null_get_video_buffer,
 +          .start_frame      = ff_null_start_frame_keep_ref,
 +          .end_frame        = end_frame, },
 +        { .name = NULL }
 +    },
 +
 +    .outputs = (const AVFilterPad[]) {
 +        { .name             = "default",
 +          .type             = AVMEDIA_TYPE_VIDEO,
 +          .request_frame    = request_frame, },
 +        { .name = NULL }
 +    },
 +};
Simple merge
Simple merge
index 80814de,0000000..0784c82
mode 100644,000000..100644
--- /dev/null
@@@ -1,391 -1,0 +1,391 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * ColorMatrix v2.2 for Avisynth 2.5.x
 + *
 + * Copyright (C) 2006-2007 Kevin Stone
 + *
 + * ColorMatrix 1.x is Copyright (C) Wilbert Dijkhof
 + *
 + * This program is free software; you can redistribute it and/or modify it
 + * under the terms of the GNU General Public License as published by the
 + * Free Software Foundation; either version 2 of the License, or (at your
 + * option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful, but
 + * OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 + * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 + * License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 + */
 +
 +/**
 + * @file
 + * ColorMatrix 2.0 is based on the original ColorMatrix filter by Wilbert
 + * Dijkhof.  It adds the ability to convert between any of: Rec.709, FCC,
 + * Rec.601, and SMPTE 240M. It also makes pre and post clipping optional,
 + * adds an option to use scaled or non-scaled coefficients, and more...
 + */
 +
 +#include <strings.h>
 +#include <float.h>
 +#include "avfilter.h"
 +#include "formats.h"
 +#include "video.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/avstring.h"
 +
 +#define NS(n) n < 0 ? (int)(n*65536.0-0.5+DBL_EPSILON) : (int)(n*65536.0+0.5)
 +#define CB(n) av_clip_uint8(n)
 +
 +static const double yuv_coeff[4][3][3] = {
 +    { { +0.7152, +0.0722, +0.2126 }, // Rec.709 (0)
 +      { -0.3850, +0.5000, -0.1150 },
 +      { -0.4540, -0.0460, +0.5000 } },
 +    { { +0.5900, +0.1100, +0.3000 }, // FCC (1)
 +      { -0.3310, +0.5000, -0.1690 },
 +      { -0.4210, -0.0790, +0.5000 } },
 +    { { +0.5870, +0.1140, +0.2990 }, // Rec.601 (ITU-R BT.470-2/SMPTE 170M) (2)
 +      { -0.3313, +0.5000, -0.1687 },
 +      { -0.4187, -0.0813, +0.5000 } },
 +    { { +0.7010, +0.0870, +0.2120 }, // SMPTE 240M (3)
 +      { -0.3840, +0.5000, -0.1160 },
 +      { -0.4450, -0.0550, +0.5000 } },
 +};
 +
 +typedef struct {
 +    int yuv_convert[16][3][3];
 +    int interlaced;
 +    int source, dest, mode;
 +    char src[256];
 +    char dst[256];
 +    int hsub, vsub;
 +} ColorMatrixContext;
 +
 +#define ma m[0][0]
 +#define mb m[0][1]
 +#define mc m[0][2]
 +#define md m[1][0]
 +#define me m[1][1]
 +#define mf m[1][2]
 +#define mg m[2][0]
 +#define mh m[2][1]
 +#define mi m[2][2]
 +
 +#define ima im[0][0]
 +#define imb im[0][1]
 +#define imc im[0][2]
 +#define imd im[1][0]
 +#define ime im[1][1]
 +#define imf im[1][2]
 +#define img im[2][0]
 +#define imh im[2][1]
 +#define imi im[2][2]
 +
 +static void inverse3x3(double im[3][3], const double m[3][3])
 +{
 +    double det = ma * (me * mi - mf * mh) - mb * (md * mi - mf * mg) + mc * (md * mh - me * mg);
 +    det = 1.0 / det;
 +    ima = det * (me * mi - mf * mh);
 +    imb = det * (mc * mh - mb * mi);
 +    imc = det * (mb * mf - mc * me);
 +    imd = det * (mf * mg - md * mi);
 +    ime = det * (ma * mi - mc * mg);
 +    imf = det * (mc * md - ma * mf);
 +    img = det * (md * mh - me * mg);
 +    imh = det * (mb * mg - ma * mh);
 +    imi = det * (ma * me - mb * md);
 +}
 +
 +static void solve_coefficients(double cm[3][3], double rgb[3][3], const double yuv[3][3])
 +{
 +    int i, j;
 +    for (i = 0; i < 3; i++)
 +        for (j = 0; j < 3; j++)
 +            cm[i][j] = yuv[i][0] * rgb[0][j] + yuv[i][1] * rgb[1][j] + yuv[i][2] * rgb[2][j];
 +}
 +
 +static void calc_coefficients(AVFilterContext *ctx)
 +{
 +    ColorMatrixContext *color = ctx->priv;
 +    double rgb_coeffd[4][3][3];
 +    double yuv_convertd[16][3][3];
 +    int v = 0;
 +    int i, j, k;
 +
 +    for (i = 0; i < 4; i++)
 +        inverse3x3(rgb_coeffd[i], yuv_coeff[i]);
 +    for (i = 0; i < 4; i++) {
 +        for (j = 0; j < 4; j++) {
 +            solve_coefficients(yuv_convertd[v], rgb_coeffd[i], yuv_coeff[j]);
 +            for (k = 0; k < 3; k++) {
 +                color->yuv_convert[v][k][0] = NS(yuv_convertd[v][k][0]);
 +                color->yuv_convert[v][k][1] = NS(yuv_convertd[v][k][1]);
 +                color->yuv_convert[v][k][2] = NS(yuv_convertd[v][k][2]);
 +            }
 +            if (color->yuv_convert[v][0][0] != 65536 || color->yuv_convert[v][1][0] != 0 ||
 +                color->yuv_convert[v][2][0] != 0) {
 +                av_log(ctx, AV_LOG_ERROR, "error calculating conversion coefficients\n");
 +            }
 +            v++;
 +        }
 +    }
 +}
 +
 +static const char *color_modes[] = {"bt709", "FCC", "bt601", "smpte240m"};
 +
 +static int get_color_mode_index(const char *name)
 +{
 +    int i;
 +
 +    for (i = 0; i < FF_ARRAY_ELEMS(color_modes); i++)
 +        if (!av_strcasecmp(color_modes[i], name))
 +            return i;
 +    return -1;
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    ColorMatrixContext *color = ctx->priv;
 +
 +    if (!args)
 +        goto usage;
 +    if (sscanf(args, "%255[^:]:%255[^:]", color->src, color->dst) != 2) {
 +    usage:
 +        av_log(ctx, AV_LOG_ERROR, "usage: <src>:<dst>\n");
 +        av_log(ctx, AV_LOG_ERROR, "possible options: bt709,bt601,smpte240m,fcc\n");
 +        return -1;
 +    }
 +
 +    color->source = get_color_mode_index(color->src);
 +    if (color->source < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "unknown color space %s\n", color->src);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    color->dest = get_color_mode_index(color->dst);
 +    if (color->dest < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "unknown color space %s\n", color->dst);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    if (color->source == color->dest) {
 +        av_log(ctx, AV_LOG_ERROR, "source and destination color space are identical\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    color->mode = color->source * 4 + color->dest;
 +
 +    calc_coefficients(ctx);
 +
 +    return 0;
 +}
 +
 +static void process_frame_uyvy422(ColorMatrixContext *color,
 +                                  AVFilterBufferRef *dst, AVFilterBufferRef *src)
 +{
 +    const unsigned char *srcp = src->data[0];
 +    const int src_pitch = src->linesize[0];
 +    const int height = src->video->h;
 +    const int width = src->video->w*2;
 +    unsigned char *dstp = dst->data[0];
 +    const int dst_pitch = dst->linesize[0];
 +    const int c2 = color->yuv_convert[color->mode][0][1];
 +    const int c3 = color->yuv_convert[color->mode][0][2];
 +    const int c4 = color->yuv_convert[color->mode][1][1];
 +    const int c5 = color->yuv_convert[color->mode][1][2];
 +    const int c6 = color->yuv_convert[color->mode][2][1];
 +    const int c7 = color->yuv_convert[color->mode][2][2];
 +    int x, y;
 +
 +    for (y = 0; y < height; y++) {
 +        for (x = 0; x < width; x += 4) {
 +            const int u = srcp[x + 0] - 128;
 +            const int v = srcp[x + 2] - 128;
 +            const int uvval = c2 * u + c3 * v + 1081344;
 +            dstp[x + 0] = CB((c4 * u + c5 * v + 8421376) >> 16);
 +            dstp[x + 1] = CB((65536 * (srcp[x + 1] - 16) + uvval) >> 16);
 +            dstp[x + 2] = CB((c6 * u + c7 * v + 8421376) >> 16);
 +            dstp[x + 3] = CB((65536 * (srcp[x + 3] - 16) + uvval) >> 16);
 +        }
 +        srcp += src_pitch;
 +        dstp += dst_pitch;
 +    }
 +}
 +
 +static void process_frame_yuv422p(ColorMatrixContext *color,
 +                                  AVFilterBufferRef *dst, AVFilterBufferRef *src)
 +{
 +    const unsigned char *srcpU = src->data[1];
 +    const unsigned char *srcpV = src->data[2];
 +    const unsigned char *srcpY = src->data[0];
 +    const int src_pitchY  = src->linesize[0];
 +    const int src_pitchUV = src->linesize[1];
 +    const int height = src->video->h;
 +    const int width = src->video->w;
 +    unsigned char *dstpU = dst->data[1];
 +    unsigned char *dstpV = dst->data[2];
 +    unsigned char *dstpY = dst->data[0];
 +    const int dst_pitchY  = dst->linesize[0];
 +    const int dst_pitchUV = dst->linesize[1];
 +    const int c2 = color->yuv_convert[color->mode][0][1];
 +    const int c3 = color->yuv_convert[color->mode][0][2];
 +    const int c4 = color->yuv_convert[color->mode][1][1];
 +    const int c5 = color->yuv_convert[color->mode][1][2];
 +    const int c6 = color->yuv_convert[color->mode][2][1];
 +    const int c7 = color->yuv_convert[color->mode][2][2];
 +    int x, y;
 +
 +    for (y = 0; y < height; y++) {
 +        for (x = 0; x < width; x += 2) {
 +            const int u = srcpU[x >> 1] - 128;
 +            const int v = srcpV[x >> 1] - 128;
 +            const int uvval = c2 * u + c3 * v + 1081344;
 +            dstpY[x + 0] = CB((65536 * (srcpY[x + 0] - 16) + uvval) >> 16);
 +            dstpY[x + 1] = CB((65536 * (srcpY[x + 1] - 16) + uvval) >> 16);
 +            dstpU[x >> 1] = CB((c4 * u + c5 * v + 8421376) >> 16);
 +            dstpV[x >> 1] = CB((c6 * u + c7 * v + 8421376) >> 16);
 +        }
 +        srcpY += src_pitchY;
 +        dstpY += dst_pitchY;
 +        srcpU += src_pitchUV;
 +        srcpV += src_pitchUV;
 +        dstpU += dst_pitchUV;
 +        dstpV += dst_pitchUV;
 +    }
 +}
 +
 +static void process_frame_yuv420p(ColorMatrixContext *color,
 +                                  AVFilterBufferRef *dst, AVFilterBufferRef *src)
 +{
 +    const unsigned char *srcpU = src->data[1];
 +    const unsigned char *srcpV = src->data[2];
 +    const unsigned char *srcpY = src->data[0];
 +    const unsigned char *srcpN = src->data[0] + src->linesize[0];
 +    const int src_pitchY  = src->linesize[0];
 +    const int src_pitchUV = src->linesize[1];
 +    const int height = src->video->h;
 +    const int width = src->video->w;
 +    unsigned char *dstpU = dst->data[1];
 +    unsigned char *dstpV = dst->data[2];
 +    unsigned char *dstpY = dst->data[0];
 +    unsigned char *dstpN = dst->data[0] + dst->linesize[0];
 +    const int dst_pitchY  = dst->linesize[0];
 +    const int dst_pitchUV = dst->linesize[1];
 +    const int c2 = color->yuv_convert[color->mode][0][1];
 +    const int c3 = color->yuv_convert[color->mode][0][2];
 +    const int c4 = color->yuv_convert[color->mode][1][1];
 +    const int c5 = color->yuv_convert[color->mode][1][2];
 +    const int c6 = color->yuv_convert[color->mode][2][1];
 +    const int c7 = color->yuv_convert[color->mode][2][2];
 +    int x, y;
 +
 +    for (y = 0; y < height; y += 2) {
 +        for (x = 0; x < width; x += 2) {
 +            const int u = srcpU[x >> 1] - 128;
 +            const int v = srcpV[x >> 1] - 128;
 +            const int uvval = c2 * u + c3 * v + 1081344;
 +            dstpY[x + 0] = CB((65536 * (srcpY[x + 0] - 16) + uvval) >> 16);
 +            dstpY[x + 1] = CB((65536 * (srcpY[x + 1] - 16) + uvval) >> 16);
 +            dstpN[x + 0] = CB((65536 * (srcpN[x + 0] - 16) + uvval) >> 16);
 +            dstpN[x + 1] = CB((65536 * (srcpN[x + 1] - 16) + uvval) >> 16);
 +            dstpU[x >> 1] = CB((c4 * u + c5 * v + 8421376) >> 16);
 +            dstpV[x >> 1] = CB((c6 * u + c7 * v + 8421376) >> 16);
 +        }
 +        srcpY += src_pitchY << 1;
 +        dstpY += dst_pitchY << 1;
 +        srcpN += src_pitchY << 1;
 +        dstpN += dst_pitchY << 1;
 +        srcpU += src_pitchUV;
 +        srcpV += src_pitchUV;
 +        dstpU += dst_pitchUV;
 +        dstpV += dst_pitchUV;
 +    }
 +}
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    ColorMatrixContext *color = ctx->priv;
 +    const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
 +
 +    color->hsub = pix_desc->log2_chroma_w;
 +    color->vsub = pix_desc->log2_chroma_h;
 +
 +    av_log(ctx, AV_LOG_INFO, "%s -> %s\n", color->src, color->dst);
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    static const enum PixelFormat pix_fmts[] = {
 +        PIX_FMT_YUV422P,
 +        PIX_FMT_YUV420P,
 +        PIX_FMT_UYVY422,
 +        PIX_FMT_NONE
 +    };
 +
 +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
 +
 +    return 0;
 +}
 +
 +static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms, int w, int h)
 +{
 +    AVFilterBufferRef *picref =
 +        ff_get_video_buffer(inlink->dst->outputs[0], perms, w, h);
 +    return picref;
 +}
 +
 +static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
 +{
 +    AVFilterBufferRef *outpicref = avfilter_ref_buffer(picref, ~0);
 +
 +    link->dst->outputs[0]->out_buf = outpicref;
 +
 +    ff_start_frame(link->dst->outputs[0], outpicref);
 +}
 +
 +static void end_frame(AVFilterLink *link)
 +{
 +    AVFilterContext *ctx = link->dst;
 +    ColorMatrixContext *color = ctx->priv;
 +    AVFilterBufferRef *out = link->dst->outputs[0]->out_buf;
 +
 +    if (link->cur_buf->format == PIX_FMT_YUV422P)
 +        process_frame_yuv422p(color, out, link->cur_buf);
 +    else if (link->cur_buf->format == PIX_FMT_YUV420P)
 +        process_frame_yuv420p(color, out, link->cur_buf);
 +    else
 +        process_frame_uyvy422(color, out, link->cur_buf);
 +
 +    ff_draw_slice(ctx->outputs[0], 0, link->dst->outputs[0]->h, 1);
 +    ff_end_frame(ctx->outputs[0]);
 +    avfilter_unref_buffer(link->cur_buf);
 +}
 +
 +static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
 +
 +AVFilter avfilter_vf_colormatrix = {
 +    .name          = "colormatrix",
 +    .description   = NULL_IF_CONFIG_SMALL("Color matrix conversion"),
 +
 +    .priv_size     = sizeof(ColorMatrixContext),
 +    .init          = init,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (AVFilterPad[]) {{ .name             = "default",
 +                                    .type             = AVMEDIA_TYPE_VIDEO,
 +                                    .config_props     = config_input,
 +                                    .start_frame      = start_frame,
 +                                    .get_video_buffer = get_video_buffer,
 +                                    .draw_slice       = null_draw_slice,
 +                                    .end_frame        = end_frame, },
 +                                  { .name = NULL }},
 +
 +    .outputs   = (AVFilterPad[]) {{ .name             = "default",
 +                                    .type             = AVMEDIA_TYPE_VIDEO, },
 +                                  { .name = NULL }},
 +};
Simple merge
Simple merge
Simple merge
index ff78f1f,0000000..10d789f
mode 100644,000000..100644
--- /dev/null
@@@ -1,561 -1,0 +1,561 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (C) 2010 Georg Martius <georg.martius@web.de>
 + * Copyright (C) 2010 Daniel G. Taylor <dan@programmer-art.org>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * fast deshake / depan video filter
 + *
 + * SAD block-matching motion compensation to fix small changes in
 + * horizontal and/or vertical shift. This filter helps remove camera shake
 + * from hand-holding a camera, bumping a tripod, moving on a vehicle, etc.
 + *
 + * Algorithm:
 + *   - For each frame with one previous reference frame
 + *       - For each block in the frame
 + *           - If contrast > threshold then find likely motion vector
 + *       - For all found motion vectors
 + *           - Find most common, store as global motion vector
 + *       - Find most likely rotation angle
 + *       - Transform image along global motion
 + *
 + * TODO:
 + *   - Fill frame edges based on previous/next reference frames
 + *   - Fill frame edges by stretching image near the edges?
 + *       - Can this be done quickly and look decent?
 + *
 + * Dark Shikari links to http://wiki.videolan.org/SoC_x264_2010#GPU_Motion_Estimation_2
 + * for an algorithm similar to what could be used here to get the gmv
 + * It requires only a couple diamond searches + fast downscaling
 + *
 + * Special thanks to Jason Kotenko for his help with the algorithm and my
 + * inability to see simple errors in C code.
 + */
 +
 +#include "avfilter.h"
 +#include "formats.h"
 +#include "video.h"
 +#include "libavutil/common.h"
 +#include "libavutil/mem.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavcodec/dsputil.h"
 +
 +#include "transform.h"
 +
 +#define CHROMA_WIDTH(link)  -((-link->w) >> av_pix_fmt_descriptors[link->format].log2_chroma_w)
 +#define CHROMA_HEIGHT(link) -((-link->h) >> av_pix_fmt_descriptors[link->format].log2_chroma_h)
 +
 +enum SearchMethod {
 +    EXHAUSTIVE,        ///< Search all possible positions
 +    SMART_EXHAUSTIVE,  ///< Search most possible positions (faster)
 +    SEARCH_COUNT
 +};
 +
 +typedef struct {
 +    int x;             ///< Horizontal shift
 +    int y;             ///< Vertical shift
 +} IntMotionVector;
 +
 +typedef struct {
 +    double x;             ///< Horizontal shift
 +    double y;             ///< Vertical shift
 +} MotionVector;
 +
 +typedef struct {
 +    MotionVector vector;  ///< Motion vector
 +    double angle;         ///< Angle of rotation
 +    double zoom;          ///< Zoom percentage
 +} Transform;
 +
 +typedef struct {
 +    AVClass av_class;
 +    AVFilterBufferRef *ref;    ///< Previous frame
 +    int rx;                    ///< Maximum horizontal shift
 +    int ry;                    ///< Maximum vertical shift
 +    enum FillMethod edge;      ///< Edge fill method
 +    int blocksize;             ///< Size of blocks to compare
 +    int contrast;              ///< Contrast threshold
 +    enum SearchMethod search;  ///< Motion search method
 +    AVCodecContext *avctx;
 +    DSPContext c;              ///< Context providing optimized SAD methods
 +    Transform last;            ///< Transform from last frame
 +    int refcount;              ///< Number of reference frames (defines averaging window)
 +    FILE *fp;
 +    Transform avg;
 +    int cw;                    ///< Crop motion search to this box
 +    int ch;
 +    int cx;
 +    int cy;
 +} DeshakeContext;
 +
 +static int cmp(const double *a, const double *b)
 +{
 +    return *a < *b ? -1 : ( *a > *b ? 1 : 0 );
 +}
 +
 +/**
 + * Cleaned mean (cuts off 20% of values to remove outliers and then averages)
 + */
 +static double clean_mean(double *values, int count)
 +{
 +    double mean = 0;
 +    int cut = count / 5;
 +    int x;
 +
 +    qsort(values, count, sizeof(double), (void*)cmp);
 +
 +    for (x = cut; x < count - cut; x++) {
 +        mean += values[x];
 +    }
 +
 +    return mean / (count - cut * 2);
 +}
 +
 +/**
 + * Find the most likely shift in motion between two frames for a given
 + * macroblock. Test each block against several shifts given by the rx
 + * and ry attributes. Searches using a simple matrix of those shifts and
 + * chooses the most likely shift by the smallest difference in blocks.
 + */
 +static void find_block_motion(DeshakeContext *deshake, uint8_t *src1,
 +                              uint8_t *src2, int cx, int cy, int stride,
 +                              IntMotionVector *mv)
 +{
 +    int x, y;
 +    int diff;
 +    int smallest = INT_MAX;
 +    int tmp, tmp2;
 +
 +    #define CMP(i, j) deshake->c.sad[0](deshake, src1 + cy * stride + cx, \
 +                                        src2 + (j) * stride + (i), stride, \
 +                                        deshake->blocksize)
 +
 +    if (deshake->search == EXHAUSTIVE) {
 +        // Compare every possible position - this is sloooow!
 +        for (y = -deshake->ry; y <= deshake->ry; y++) {
 +            for (x = -deshake->rx; x <= deshake->rx; x++) {
 +                diff = CMP(cx - x, cy - y);
 +                if (diff < smallest) {
 +                    smallest = diff;
 +                    mv->x = x;
 +                    mv->y = y;
 +                }
 +            }
 +        }
 +    } else if (deshake->search == SMART_EXHAUSTIVE) {
 +        // Compare every other possible position and find the best match
 +        for (y = -deshake->ry + 1; y < deshake->ry - 2; y += 2) {
 +            for (x = -deshake->rx + 1; x < deshake->rx - 2; x += 2) {
 +                diff = CMP(cx - x, cy - y);
 +                if (diff < smallest) {
 +                    smallest = diff;
 +                    mv->x = x;
 +                    mv->y = y;
 +                }
 +            }
 +        }
 +
 +        // Hone in on the specific best match around the match we found above
 +        tmp = mv->x;
 +        tmp2 = mv->y;
 +
 +        for (y = tmp2 - 1; y <= tmp2 + 1; y++) {
 +            for (x = tmp - 1; x <= tmp + 1; x++) {
 +                if (x == tmp && y == tmp2)
 +                    continue;
 +
 +                diff = CMP(cx - x, cy - y);
 +                if (diff < smallest) {
 +                    smallest = diff;
 +                    mv->x = x;
 +                    mv->y = y;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (smallest > 512) {
 +        mv->x = -1;
 +        mv->y = -1;
 +    }
 +    emms_c();
 +    //av_log(NULL, AV_LOG_ERROR, "%d\n", smallest);
 +    //av_log(NULL, AV_LOG_ERROR, "Final: (%d, %d) = %d x %d\n", cx, cy, mv->x, mv->y);
 +}
 +
 +/**
 + * Find the contrast of a given block. When searching for global motion we
 + * really only care about the high contrast blocks, so using this method we
 + * can actually skip blocks we don't care much about.
 + */
 +static int block_contrast(uint8_t *src, int x, int y, int stride, int blocksize)
 +{
 +    int highest = 0;
 +    int lowest = 0;
 +    int i, j, pos;
 +
 +    for (i = 0; i <= blocksize * 2; i++) {
 +        // We use a width of 16 here to match the libavcodec sad functions
 +        for (j = 0; i <= 15; i++) {
 +            pos = (y - i) * stride + (x - j);
 +            if (src[pos] < lowest)
 +                lowest = src[pos];
 +            else if (src[pos] > highest) {
 +                highest = src[pos];
 +            }
 +        }
 +    }
 +
 +    return highest - lowest;
 +}
 +
 +/**
 + * Find the rotation for a given block.
 + */
 +static double block_angle(int x, int y, int cx, int cy, IntMotionVector *shift)
 +{
 +    double a1, a2, diff;
 +
 +    a1 = atan2(y - cy, x - cx);
 +    a2 = atan2(y - cy + shift->y, x - cx + shift->x);
 +
 +    diff = a2 - a1;
 +
 +    return (diff > M_PI)  ? diff - 2 * M_PI :
 +           (diff < -M_PI) ? diff + 2 * M_PI :
 +           diff;
 +}
 +
 +/**
 + * Find the estimated global motion for a scene given the most likely shift
 + * for each block in the frame. The global motion is estimated to be the
 + * same as the motion from most blocks in the frame, so if most blocks
 + * move one pixel to the right and two pixels down, this would yield a
 + * motion vector (1, -2).
 + */
 +static void find_motion(DeshakeContext *deshake, uint8_t *src1, uint8_t *src2,
 +                        int width, int height, int stride, Transform *t)
 +{
 +    int x, y;
 +    IntMotionVector mv = {0, 0};
 +    int counts[128][128];
 +    int count_max_value = 0;
 +    int contrast;
 +
 +    int pos;
 +    double *angles = av_malloc(sizeof(*angles) * width * height / (16 * deshake->blocksize));
 +    int center_x = 0, center_y = 0;
 +    double p_x, p_y;
 +
 +    // Reset counts to zero
 +    for (x = 0; x < deshake->rx * 2 + 1; x++) {
 +        for (y = 0; y < deshake->ry * 2 + 1; y++) {
 +            counts[x][y] = 0;
 +        }
 +    }
 +
 +    pos = 0;
 +    // Find motion for every block and store the motion vector in the counts
 +    for (y = deshake->ry; y < height - deshake->ry - (deshake->blocksize * 2); y += deshake->blocksize * 2) {
 +        // We use a width of 16 here to match the libavcodec sad functions
 +        for (x = deshake->rx; x < width - deshake->rx - 16; x += 16) {
 +            // If the contrast is too low, just skip this block as it probably
 +            // won't be very useful to us.
 +            contrast = block_contrast(src2, x, y, stride, deshake->blocksize);
 +            if (contrast > deshake->contrast) {
 +                //av_log(NULL, AV_LOG_ERROR, "%d\n", contrast);
 +                find_block_motion(deshake, src1, src2, x, y, stride, &mv);
 +                if (mv.x != -1 && mv.y != -1) {
 +                    counts[mv.x + deshake->rx][mv.y + deshake->ry] += 1;
 +                    if (x > deshake->rx && y > deshake->ry)
 +                        angles[pos++] = block_angle(x, y, 0, 0, &mv);
 +
 +                    center_x += mv.x;
 +                    center_y += mv.y;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (pos) {
 +         center_x /= pos;
 +         center_y /= pos;
 +         t->angle = clean_mean(angles, pos);
 +         if (t->angle < 0.001)
 +              t->angle = 0;
 +    } else {
 +         t->angle = 0;
 +    }
 +
 +    // Find the most common motion vector in the frame and use it as the gmv
 +    for (y = deshake->ry * 2; y >= 0; y--) {
 +        for (x = 0; x < deshake->rx * 2 + 1; x++) {
 +            //av_log(NULL, AV_LOG_ERROR, "%5d ", counts[x][y]);
 +            if (counts[x][y] > count_max_value) {
 +                t->vector.x = x - deshake->rx;
 +                t->vector.y = y - deshake->ry;
 +                count_max_value = counts[x][y];
 +            }
 +        }
 +        //av_log(NULL, AV_LOG_ERROR, "\n");
 +    }
 +
 +    p_x = (center_x - width / 2);
 +    p_y = (center_y - height / 2);
 +    t->vector.x += (cos(t->angle)-1)*p_x  - sin(t->angle)*p_y;
 +    t->vector.y += sin(t->angle)*p_x  + (cos(t->angle)-1)*p_y;
 +
 +    // Clamp max shift & rotation?
 +    t->vector.x = av_clipf(t->vector.x, -deshake->rx * 2, deshake->rx * 2);
 +    t->vector.y = av_clipf(t->vector.y, -deshake->ry * 2, deshake->ry * 2);
 +    t->angle = av_clipf(t->angle, -0.1, 0.1);
 +
 +    //av_log(NULL, AV_LOG_ERROR, "%d x %d\n", avg->x, avg->y);
 +    av_free(angles);
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    DeshakeContext *deshake = ctx->priv;
 +    char filename[256] = {0};
 +
 +    deshake->rx = 16;
 +    deshake->ry = 16;
 +    deshake->edge = FILL_MIRROR;
 +    deshake->blocksize = 8;
 +    deshake->contrast = 125;
 +    deshake->search = EXHAUSTIVE;
 +    deshake->refcount = 20;
 +
 +    deshake->cw = -1;
 +    deshake->ch = -1;
 +    deshake->cx = -1;
 +    deshake->cy = -1;
 +
 +    if (args) {
 +        sscanf(args, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%255s",
 +               &deshake->cx, &deshake->cy, &deshake->cw, &deshake->ch,
 +               &deshake->rx, &deshake->ry, (int *)&deshake->edge,
 +               &deshake->blocksize, &deshake->contrast, (int *)&deshake->search, filename);
 +
 +        deshake->blocksize /= 2;
 +
 +        deshake->rx = av_clip(deshake->rx, 0, 64);
 +        deshake->ry = av_clip(deshake->ry, 0, 64);
 +        deshake->edge = av_clip(deshake->edge, FILL_BLANK, FILL_COUNT - 1);
 +        deshake->blocksize = av_clip(deshake->blocksize, 4, 128);
 +        deshake->contrast = av_clip(deshake->contrast, 1, 255);
 +        deshake->search = av_clip(deshake->search, EXHAUSTIVE, SEARCH_COUNT - 1);
 +
 +    }
 +    if (*filename)
 +        deshake->fp = fopen(filename, "w");
 +    if (deshake->fp)
 +        fwrite("Ori x, Avg x, Fin x, Ori y, Avg y, Fin y, Ori angle, Avg angle, Fin angle, Ori zoom, Avg zoom, Fin zoom\n", sizeof(char), 104, deshake->fp);
 +
 +    // Quadword align left edge of box for MMX code, adjust width if necessary
 +    // to keep right margin
 +    if (deshake->cx > 0) {
 +        deshake->cw += deshake->cx - (deshake->cx & ~15);
 +        deshake->cx &= ~15;
 +    }
 +
 +    av_log(ctx, AV_LOG_INFO, "cx: %d, cy: %d, cw: %d, ch: %d, rx: %d, ry: %d, edge: %d blocksize: %d contrast: %d search: %d\n",
 +           deshake->cx, deshake->cy, deshake->cw, deshake->ch,
 +           deshake->rx, deshake->ry, deshake->edge, deshake->blocksize * 2, deshake->contrast, deshake->search);
 +
 +    return 0;
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    enum PixelFormat pix_fmts[] = {
 +        PIX_FMT_YUV420P,  PIX_FMT_YUV422P,  PIX_FMT_YUV444P,  PIX_FMT_YUV410P,
 +        PIX_FMT_YUV411P,  PIX_FMT_YUV440P,  PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P,
 +        PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P, PIX_FMT_NONE
 +    };
 +
 +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
 +
 +    return 0;
 +}
 +
 +static int config_props(AVFilterLink *link)
 +{
 +    DeshakeContext *deshake = link->dst->priv;
 +
 +    deshake->ref = NULL;
 +    deshake->last.vector.x = 0;
 +    deshake->last.vector.y = 0;
 +    deshake->last.angle = 0;
 +    deshake->last.zoom = 0;
 +
 +    deshake->avctx = avcodec_alloc_context3(NULL);
 +    dsputil_init(&deshake->c, deshake->avctx);
 +
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    DeshakeContext *deshake = ctx->priv;
 +
 +    avfilter_unref_buffer(deshake->ref);
 +    if (deshake->fp)
 +        fclose(deshake->fp);
 +    if (deshake->avctx)
 +        avcodec_close(deshake->avctx);
 +    av_freep(&deshake->avctx);
 +}
 +
 +static void end_frame(AVFilterLink *link)
 +{
 +    DeshakeContext *deshake = link->dst->priv;
 +    AVFilterBufferRef *in  = link->cur_buf;
 +    AVFilterBufferRef *out = link->dst->outputs[0]->out_buf;
 +    Transform t = {{0},0}, orig = {{0},0};
 +    float matrix[9];
 +    float alpha = 2.0 / deshake->refcount;
 +    char tmp[256];
 +
 +    if (deshake->cx < 0 || deshake->cy < 0 || deshake->cw < 0 || deshake->ch < 0) {
 +        // Find the most likely global motion for the current frame
 +        find_motion(deshake, (deshake->ref == NULL) ? in->data[0] : deshake->ref->data[0], in->data[0], link->w, link->h, in->linesize[0], &t);
 +    } else {
 +        uint8_t *src1 = (deshake->ref == NULL) ? in->data[0] : deshake->ref->data[0];
 +        uint8_t *src2 = in->data[0];
 +
 +        deshake->cx = FFMIN(deshake->cx, link->w);
 +        deshake->cy = FFMIN(deshake->cy, link->h);
 +
 +        if ((unsigned)deshake->cx + (unsigned)deshake->cw > link->w) deshake->cw = link->w - deshake->cx;
 +        if ((unsigned)deshake->cy + (unsigned)deshake->ch > link->h) deshake->ch = link->h - deshake->cy;
 +
 +        // Quadword align right margin
 +        deshake->cw &= ~15;
 +
 +        src1 += deshake->cy * in->linesize[0] + deshake->cx;
 +        src2 += deshake->cy * in->linesize[0] + deshake->cx;
 +
 +        find_motion(deshake, src1, src2, deshake->cw, deshake->ch, in->linesize[0], &t);
 +    }
 +
 +
 +    // Copy transform so we can output it later to compare to the smoothed value
 +    orig.vector.x = t.vector.x;
 +    orig.vector.y = t.vector.y;
 +    orig.angle = t.angle;
 +    orig.zoom = t.zoom;
 +
 +    // Generate a one-sided moving exponential average
 +    deshake->avg.vector.x = alpha * t.vector.x + (1.0 - alpha) * deshake->avg.vector.x;
 +    deshake->avg.vector.y = alpha * t.vector.y + (1.0 - alpha) * deshake->avg.vector.y;
 +    deshake->avg.angle = alpha * t.angle + (1.0 - alpha) * deshake->avg.angle;
 +    deshake->avg.zoom = alpha * t.zoom + (1.0 - alpha) * deshake->avg.zoom;
 +
 +    // Remove the average from the current motion to detect the motion that
 +    // is not on purpose, just as jitter from bumping the camera
 +    t.vector.x -= deshake->avg.vector.x;
 +    t.vector.y -= deshake->avg.vector.y;
 +    t.angle -= deshake->avg.angle;
 +    t.zoom -= deshake->avg.zoom;
 +
 +    // Invert the motion to undo it
 +    t.vector.x *= -1;
 +    t.vector.y *= -1;
 +    t.angle *= -1;
 +
 +    // Write statistics to file
 +    if (deshake->fp) {
 +        snprintf(tmp, 256, "%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f\n", orig.vector.x, deshake->avg.vector.x, t.vector.x, orig.vector.y, deshake->avg.vector.y, t.vector.y, orig.angle, deshake->avg.angle, t.angle, orig.zoom, deshake->avg.zoom, t.zoom);
 +        fwrite(tmp, sizeof(char), strlen(tmp), deshake->fp);
 +    }
 +
 +    // Turn relative current frame motion into absolute by adding it to the
 +    // last absolute motion
 +    t.vector.x += deshake->last.vector.x;
 +    t.vector.y += deshake->last.vector.y;
 +    t.angle += deshake->last.angle;
 +    t.zoom += deshake->last.zoom;
 +
 +    // Shrink motion by 10% to keep things centered in the camera frame
 +    t.vector.x *= 0.9;
 +    t.vector.y *= 0.9;
 +    t.angle *= 0.9;
 +
 +    // Store the last absolute motion information
 +    deshake->last.vector.x = t.vector.x;
 +    deshake->last.vector.y = t.vector.y;
 +    deshake->last.angle = t.angle;
 +    deshake->last.zoom = t.zoom;
 +
 +    // Generate a luma transformation matrix
 +    avfilter_get_matrix(t.vector.x, t.vector.y, t.angle, 1.0 + t.zoom / 100.0, matrix);
 +
 +    // Transform the luma plane
 +    avfilter_transform(in->data[0], out->data[0], in->linesize[0], out->linesize[0], link->w, link->h, matrix, INTERPOLATE_BILINEAR, deshake->edge);
 +
 +    // Generate a chroma transformation matrix
 +    avfilter_get_matrix(t.vector.x / (link->w / CHROMA_WIDTH(link)), t.vector.y / (link->h / CHROMA_HEIGHT(link)), t.angle, 1.0 + t.zoom / 100.0, matrix);
 +
 +    // Transform the chroma planes
 +    avfilter_transform(in->data[1], out->data[1], in->linesize[1], out->linesize[1], CHROMA_WIDTH(link), CHROMA_HEIGHT(link), matrix, INTERPOLATE_BILINEAR, deshake->edge);
 +    avfilter_transform(in->data[2], out->data[2], in->linesize[2], out->linesize[2], CHROMA_WIDTH(link), CHROMA_HEIGHT(link), matrix, INTERPOLATE_BILINEAR, deshake->edge);
 +
 +    // Store the current frame as the reference frame for calculating the
 +    // motion of the next frame
 +    if (deshake->ref != NULL)
 +        avfilter_unref_buffer(deshake->ref);
 +
 +    // Cleanup the old reference frame
 +    deshake->ref = in;
 +
 +    // Draw the transformed frame information
 +    ff_draw_slice(link->dst->outputs[0], 0, link->h, 1);
 +    ff_end_frame(link->dst->outputs[0]);
 +    avfilter_unref_buffer(out);
 +}
 +
 +static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
 +{
 +}
 +
 +AVFilter avfilter_vf_deshake = {
 +    .name      = "deshake",
 +    .description = NULL_IF_CONFIG_SMALL("Stabilize shaky video."),
 +
 +    .priv_size = sizeof(DeshakeContext),
 +
 +    .init = init,
 +    .uninit = uninit,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name       = "default",
 +                                    .type             = AVMEDIA_TYPE_VIDEO,
 +                                    .draw_slice       = draw_slice,
 +                                    .end_frame        = end_frame,
 +                                    .config_props     = config_props,
 +                                    .min_perms        = AV_PERM_READ, },
 +                                  { .name = NULL}},
 +
 +    .outputs   = (const AVFilterPad[]) {{ .name       = "default",
 +                                    .type             = AVMEDIA_TYPE_VIDEO, },
 +                                  { .name = NULL}},
 +};
Simple merge
@@@ -294,92 -280,7 +294,92 @@@ error
      return ret;
  }
  
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +static int load_font_file(AVFilterContext *ctx, const char *path, int index,
 +                          const char **error)
 +{
 +    DrawTextContext *dtext = ctx->priv;
 +    int err;
 +
 +    err = FT_New_Face(dtext->library, path, index, &dtext->face);
 +    if (err) {
 +        *error = FT_ERRMSG(err);
 +        return AVERROR(EINVAL);
 +    }
 +    return 0;
 +}
 +
 +#if CONFIG_FONTCONFIG
 +static int load_font_fontconfig(AVFilterContext *ctx, const char **error)
 +{
 +    DrawTextContext *dtext = ctx->priv;
 +    FcConfig *fontconfig;
 +    FcPattern *pattern, *fpat;
 +    FcResult result = FcResultMatch;
 +    FcChar8 *filename;
 +    int err, index;
 +    double size;
 +
 +    fontconfig = FcInitLoadConfigAndFonts();
 +    if (!fontconfig) {
 +        *error = "impossible to init fontconfig\n";
 +        return AVERROR(EINVAL);
 +    }
 +    pattern = FcNameParse(dtext->fontfile ? dtext->fontfile :
 +                          (uint8_t *)(intptr_t)"default");
 +    if (!pattern) {
 +        *error = "could not parse fontconfig pattern";
 +        return AVERROR(EINVAL);
 +    }
 +    if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
 +        *error = "could not substitue fontconfig options"; /* very unlikely */
 +        return AVERROR(EINVAL);
 +    }
 +    FcDefaultSubstitute(pattern);
 +    fpat = FcFontMatch(fontconfig, pattern, &result);
 +    if (!fpat || result != FcResultMatch) {
 +        *error = "impossible to find a matching font";
 +        return AVERROR(EINVAL);
 +    }
 +    if (FcPatternGetString (fpat, FC_FILE,  0, &filename) != FcResultMatch ||
 +        FcPatternGetInteger(fpat, FC_INDEX, 0, &index   ) != FcResultMatch ||
 +        FcPatternGetDouble (fpat, FC_SIZE,  0, &size    ) != FcResultMatch) {
 +        *error = "impossible to find font information";
 +        return AVERROR(EINVAL);
 +    }
 +    av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
 +    if (!dtext->fontsize)
 +        dtext->fontsize = size + 0.5;
 +    err = load_font_file(ctx, filename, index, error);
 +    if (err)
 +        return err;
 +    FcPatternDestroy(fpat);
 +    FcPatternDestroy(pattern);
 +    FcConfigDestroy(fontconfig);
 +    return 0;
 +}
 +#endif
 +
 +static int load_font(AVFilterContext *ctx)
 +{
 +    DrawTextContext *dtext = ctx->priv;
 +    int err;
 +    const char *error = "unknown error\n";
 +
 +    /* load the face, and set up the encoding, which is by default UTF-8 */
 +    err = load_font_file(ctx, dtext->fontfile, 0, &error);
 +    if (!err)
 +        return 0;
 +#if CONFIG_FONTCONFIG
 +    err = load_font_fontconfig(ctx, &error);
 +    if (!err)
 +        return 0;
 +#endif
 +    av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
 +           dtext->fontfile, error);
 +    return err;
 +}
 +
+ static av_cold int init(AVFilterContext *ctx, const char *args)
  {
      int err;
      DrawTextContext *dtext = ctx->priv;
@@@ -507,121 -411,34 +507,121 @@@ static int glyph_enu_free(void *opaque
  static av_cold void uninit(AVFilterContext *ctx)
  {
      DrawTextContext *dtext = ctx->priv;
 -    int i;
  
 -    av_freep(&dtext->fontfile);
 -    av_freep(&dtext->text);
 -    av_freep(&dtext->expanded_text);
 -    av_freep(&dtext->fontcolor_string);
 -    av_freep(&dtext->boxcolor_string);
 +    av_expr_free(dtext->x_pexpr); dtext->x_pexpr = NULL;
 +    av_expr_free(dtext->y_pexpr); dtext->y_pexpr = NULL;
 +    av_expr_free(dtext->draw_pexpr); dtext->draw_pexpr = NULL;
 +    av_opt_free(dtext);
 +
      av_freep(&dtext->positions);
 -    av_freep(&dtext->shadowcolor_string);
 +    dtext->nb_positions = 0;
 +
      av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
      av_tree_destroy(dtext->glyphs);
 -    dtext->glyphs = 0;
 +    dtext->glyphs = NULL;
 +
      FT_Done_Face(dtext->face);
      FT_Done_FreeType(dtext->library);
 +}
 +
 +static inline int is_newline(uint32_t c)
 +{
 +    return c == '\n' || c == '\r' || c == '\f' || c == '\v';
 +}
 +
 +static int config_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    DrawTextContext *dtext = ctx->priv;
 +    int ret;
 +
 +    ff_draw_init(&dtext->dc, inlink->format, 0);
 +    ff_draw_color(&dtext->dc, &dtext->fontcolor,   dtext->fontcolor.rgba);
 +    ff_draw_color(&dtext->dc, &dtext->shadowcolor, dtext->shadowcolor.rgba);
 +    ff_draw_color(&dtext->dc, &dtext->boxcolor,    dtext->boxcolor.rgba);
 +
 +    dtext->var_values[VAR_w]     = dtext->var_values[VAR_W]     = dtext->var_values[VAR_MAIN_W] = inlink->w;
 +    dtext->var_values[VAR_h]     = dtext->var_values[VAR_H]     = dtext->var_values[VAR_MAIN_H] = inlink->h;
 +    dtext->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
 +    dtext->var_values[VAR_DAR]   = (double)inlink->w / inlink->h * dtext->var_values[VAR_SAR];
 +    dtext->var_values[VAR_HSUB]  = 1 << dtext->dc.hsub_max;
 +    dtext->var_values[VAR_VSUB]  = 1 << dtext->dc.vsub_max;
 +    dtext->var_values[VAR_X]     = NAN;
 +    dtext->var_values[VAR_Y]     = NAN;
 +    if (!dtext->reinit)
 +        dtext->var_values[VAR_N] = 0;
 +    dtext->var_values[VAR_T]     = NAN;
 +
 +    av_lfg_init(&dtext->prng, av_get_random_seed());
 +
 +    if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names,
 +                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
 +        (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names,
 +                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
 +        (ret = av_expr_parse(&dtext->draw_pexpr, dtext->draw_expr, var_names,
 +                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
 +
 +        return AVERROR(EINVAL);
 +
 +    return 0;
 +}
 +
 +static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
 +{
 +    DrawTextContext *dtext = ctx->priv;
  
 -    for (i = 0; i < 4; i++) {
 -        av_freep(&dtext->box_line[i]);
 -        dtext->pixel_step[i] = 0;
 +    if (!strcmp(cmd, "reinit")) {
 +        int ret;
 +        uninit(ctx);
 +        dtext->reinit = 1;
-         if ((ret = init(ctx, arg, NULL)) < 0)
++        if ((ret = init(ctx, arg)) < 0)
 +            return ret;
 +        return config_input(ctx->inputs[0]);
      }
  
 +    return AVERROR(ENOSYS);
  }
  
 -static inline int is_newline(uint32_t c)
 +static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
 +                       int width, int height, const uint8_t rgbcolor[4], FFDrawColor *color, int x, int y)
  {
 -    return c == '\n' || c == '\r' || c == '\f' || c == '\v';
 +    char *text = dtext->expanded_text;
 +    uint32_t code = 0;
 +    int i, x1, y1;
 +    uint8_t *p;
 +    Glyph *glyph = NULL;
 +
 +    for (i = 0, p = text; *p; i++) {
 +        Glyph dummy = { 0 };
 +        GET_UTF8(code, *p++, continue;);
 +
 +        /* skip new line chars, just go to new line */
 +        if (code == '\n' || code == '\r' || code == '\t')
 +            continue;
 +
 +        dummy.code = code;
 +        glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
 +
 +        if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
 +            glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
 +            return AVERROR(EINVAL);
 +
 +        x1 = dtext->positions[i].x+dtext->x+x;
 +        y1 = dtext->positions[i].y+dtext->y+y;
 +
 +        ff_blend_mask(&dtext->dc, color,
 +                      picref->data, picref->linesize, width, height,
 +                      glyph->bitmap.buffer, glyph->bitmap.pitch,
 +                      glyph->bitmap.width, glyph->bitmap.rows,
 +                      glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
 +                      0, x1, y1);
 +    }
 +
 +    return 0;
  }
  
 -static int dtext_prepare_text(AVFilterContext *ctx)
 +static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
 +                     int width, int height)
  {
      DrawTextContext *dtext = ctx->priv;
      uint32_t code = 0, prev_code = 0;
  #include "internal.h"
  #include "video.h"
  
 +#define R 0
 +#define G 1
 +#define B 2
 +#define A 3
 +
 +#define Y 0
 +#define U 1
 +#define V 2
 +
  typedef struct {
 +    const AVClass *class;
      int factor, fade_per_frame;
 -    unsigned int frame_index, start_frame, stop_frame;
 +    unsigned int frame_index, start_frame, stop_frame, nb_frames;
      int hsub, vsub, bpp;
 +    unsigned int black_level, black_level_scaled;
 +    uint8_t is_packed_rgb;
 +    uint8_t rgba_map[4];
 +    int alpha;
 +
 +    char *type;
  } FadeContext;
  
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +#define OFFSET(x) offsetof(FadeContext, x)
 +
 +static const AVOption fade_options[] = {
 +    { "type",        "set the fade direction",                     OFFSET(type),        AV_OPT_TYPE_STRING, {.str = "in" }, CHAR_MIN, CHAR_MAX },
 +    { "t",           "set the fade direction",                     OFFSET(type),        AV_OPT_TYPE_STRING, {.str = "in" }, CHAR_MIN, CHAR_MAX },
 +    { "start_frame", "set expression of frame to start fading",    OFFSET(start_frame), AV_OPT_TYPE_INT, {.dbl = 0    }, 0, INT_MAX },
 +    { "s",           "set expression of frame to start fading",    OFFSET(start_frame), AV_OPT_TYPE_INT, {.dbl = 0    }, 0, INT_MAX },
 +    { "nb_frames",   "set expression for fade duration in frames", OFFSET(nb_frames),   AV_OPT_TYPE_INT, {.dbl = 25   }, 0, INT_MAX },
 +    { "n",           "set expression for fade duration in frames", OFFSET(nb_frames),   AV_OPT_TYPE_INT, {.dbl = 25   }, 0, INT_MAX },
 +    { "alpha",       "fade alpha if it is available on the input", OFFSET(alpha),       AV_OPT_TYPE_INT, {.dbl = 0    }, 0,       1 },
 +    {NULL},
 +};
 +
 +AVFILTER_DEFINE_CLASS(fade);
 +
+ static av_cold int init(AVFilterContext *ctx, const char *args)
  {
      FadeContext *fade = ctx->priv;
 -    unsigned int nb_frames;
 -    char in_out[4];
 +    int ret = 0;
 +    char *args1, *expr, *bufptr = NULL;
  
 -    if (!args ||
 -        sscanf(args, " %3[^:]:%u:%u", in_out, &fade->start_frame, &nb_frames) != 3) {
 -        av_log(ctx, AV_LOG_ERROR,
 -               "Expected 3 arguments '(in|out):#:#':'%s'\n", args);
 -        return AVERROR(EINVAL);
 +    fade->class = &fade_class;
 +    av_opt_set_defaults(fade);
 +
 +    if (!(args1 = av_strdup(args))) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
      }
  
 -    nb_frames = nb_frames ? nb_frames : 1;
 -    fade->fade_per_frame = (1 << 16) / nb_frames;
 -    if (!strcmp(in_out, "in"))
 +    if (expr = av_strtok(args1, ":", &bufptr)) {
 +        av_free(fade->type);
 +        if (!(fade->type = av_strdup(expr))) {
 +            ret = AVERROR(ENOMEM);
 +            goto end;
 +        }
 +    }
 +    if (expr = av_strtok(NULL, ":", &bufptr)) {
 +        if ((ret = av_opt_set(fade, "start_frame", expr, 0)) < 0) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Invalid value '%s' for start_frame option\n", expr);
 +            return ret;
 +        }
 +    }
 +    if (expr = av_strtok(NULL, ":", &bufptr)) {
 +        if ((ret = av_opt_set(fade, "nb_frames", expr, 0)) < 0) {
 +            av_log(ctx, AV_LOG_ERROR,
 +                   "Invalid value '%s' for nb_frames option\n", expr);
 +            return ret;
 +        }
 +    }
 +
 +    if (bufptr && (ret = av_set_options_string(fade, bufptr, "=", ":")) < 0)
 +        goto end;
 +
 +    fade->fade_per_frame = (1 << 16) / fade->nb_frames;
 +    if (!strcmp(fade->type, "in"))
          fade->factor = 0;
 -    else if (!strcmp(in_out, "out")) {
 +    else if (!strcmp(fade->type, "out")) {
          fade->fade_per_frame = -fade->fade_per_frame;
          fade->factor = (1 << 16);
      } else {
Simple merge
Simple merge
@@@ -60,9 -56,14 +60,9 @@@ static const AVOption fps_options[] = 
      { NULL },
  };
  
 -static const AVClass class = {
 -    .class_name = "FPS filter",
 -    .item_name  = av_default_item_name,
 -    .option     = options,
 -    .version    = LIBAVUTIL_VERSION_INT,
 -};
 +AVFILTER_DEFINE_CLASS(fps);
  
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
+ static av_cold int init(AVFilterContext *ctx, const char *args)
  {
      FPSContext *s = ctx->priv;
      int ret;
Simple merge
Simple merge
Simple merge
index 1dcf7da,0000000..d192be7
mode 100644,000000..100644
--- /dev/null
@@@ -1,338 -1,0 +1,338 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (C) 2012 Michael Niedermayer <michaelni@gmx.at>
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +#include "libavutil/cpu.h"
 +#include "libavutil/common.h"
 +#include "libavutil/pixdesc.h"
 +#include "avfilter.h"
 +#include "internal.h"
 +
 +#undef NDEBUG
 +#include <assert.h>
 +
 +#define HIST_SIZE 4
 +
 +typedef enum {
 +    TFF,
 +    BFF,
 +    PROGRSSIVE,
 +    UNDETERMINED,
 +} Type;
 +
 +typedef struct {
 +    float interlace_threshold;
 +    float progressive_threshold;
 +
 +    Type last_type;
 +    Type prestat[4];
 +    Type poststat[4];
 +
 +    uint8_t history[HIST_SIZE];
 +
 +    AVFilterBufferRef *cur;
 +    AVFilterBufferRef *next;
 +    AVFilterBufferRef *prev;
 +    AVFilterBufferRef *out;
 +    int (*filter_line)(const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w);
 +
 +    const AVPixFmtDescriptor *csp;
 +} IDETContext;
 +
 +static const char *type2str(Type type)
 +{
 +    switch(type) {
 +        case TFF         : return "Top Field First   ";
 +        case BFF         : return "Bottom Field First";
 +        case PROGRSSIVE  : return "Progressive       ";
 +        case UNDETERMINED: return "Undetermined      ";
 +    }
 +    return NULL;
 +}
 +
 +static int filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
 +{
 +    int x;
 +    int ret=0;
 +
 +    for(x=0; x<w; x++){
 +        ret += FFABS((*a++ + *c++) - 2 * *b++);
 +    }
 +
 +    return ret;
 +}
 +
 +static int filter_line_c_16bit(const uint16_t *a, const uint16_t *b, const uint16_t *c, int w)
 +{
 +    int x;
 +    int ret=0;
 +
 +    for(x=0; x<w; x++){
 +        ret += FFABS((*a++ + *c++) - 2 * *b++);
 +    }
 +
 +    return ret;
 +}
 +
 +static void filter(AVFilterContext *ctx)
 +{
 +    IDETContext *idet = ctx->priv;
 +    int y, i;
 +    int64_t alpha[2]={0};
 +    int64_t delta=0;
 +    Type type, best_type;
 +    int match = 0;
 +
 +    for (i = 0; i < idet->csp->nb_components; i++) {
 +        int w = idet->cur->video->w;
 +        int h = idet->cur->video->h;
 +        int refs = idet->cur->linesize[i];
 +
 +        if (i && i<3) {
 +            w >>= idet->csp->log2_chroma_w;
 +            h >>= idet->csp->log2_chroma_h;
 +        }
 +
 +        for (y = 2; y < h - 2; y++) {
 +            uint8_t *prev = &idet->prev->data[i][y*refs];
 +            uint8_t *cur  = &idet->cur ->data[i][y*refs];
 +            uint8_t *next = &idet->next->data[i][y*refs];
 +            alpha[ y   &1] += idet->filter_line(cur-refs, prev, cur+refs, w);
 +            alpha[(y^1)&1] += idet->filter_line(cur-refs, next, cur+refs, w);
 +            delta          += idet->filter_line(cur-refs,  cur, cur+refs, w);
 +        }
 +    }
 +#if HAVE_MMX
 +    __asm__ volatile("emms \n\t" : : : "memory");
 +#endif
 +
 +    if      (alpha[0] / (float)alpha[1] > idet->interlace_threshold){
 +        type = TFF;
 +    }else if(alpha[1] / (float)alpha[0] > idet->interlace_threshold){
 +        type = BFF;
 +    }else if(alpha[1] / (float)delta    > idet->progressive_threshold){
 +        type = PROGRSSIVE;
 +    }else{
 +        type = UNDETERMINED;
 +    }
 +
 +    memmove(idet->history+1, idet->history, HIST_SIZE-1);
 +    idet->history[0] = type;
 +    best_type = UNDETERMINED;
 +    for(i=0; i<HIST_SIZE; i++){
 +        if(idet->history[i] != UNDETERMINED){
 +            if(best_type == UNDETERMINED)
 +                best_type = idet->history[i];
 +
 +            if(idet->history[i] == best_type) {
 +                match++;
 +            }else{
 +                match=0;
 +                break;
 +            }
 +        }
 +    }
 +    if(idet->last_type == UNDETERMINED){
 +        if(match  ) idet->last_type = best_type;
 +    }else{
 +        if(match>2) idet->last_type = best_type;
 +    }
 +
 +    if      (idet->last_type == TFF){
 +        idet->cur->video->top_field_first = 1;
 +        idet->cur->video->interlaced = 1;
 +    }else if(idet->last_type == BFF){
 +        idet->cur->video->top_field_first = 0;
 +        idet->cur->video->interlaced = 1;
 +    }else if(idet->last_type == PROGRSSIVE){
 +        idet->cur->video->interlaced = 0;
 +    }
 +
 +    idet->prestat [           type] ++;
 +    idet->poststat[idet->last_type] ++;
 +    av_log(ctx, AV_LOG_DEBUG, "Single frame:%s, Multi frame:%s\n", type2str(type), type2str(idet->last_type));
 +}
 +
 +static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
 +{
 +    AVFilterContext *ctx = link->dst;
 +    IDETContext *idet = ctx->priv;
 +
 +    if (idet->prev)
 +        avfilter_unref_buffer(idet->prev);
 +    idet->prev = idet->cur;
 +    idet->cur  = idet->next;
 +    idet->next = picref;
 +
 +    if (!idet->cur)
 +        return;
 +
 +    if (!idet->prev)
 +        idet->prev = avfilter_ref_buffer(idet->cur, AV_PERM_READ);
 +
 +    ff_start_frame(ctx->outputs[0], avfilter_ref_buffer(idet->cur, AV_PERM_READ));
 +}
 +
 +static void end_frame(AVFilterLink *link)
 +{
 +    AVFilterContext *ctx = link->dst;
 +    IDETContext *idet = ctx->priv;
 +
 +    if (!idet->cur)
 +        return;
 +
 +    if (!idet->csp)
 +        idet->csp = &av_pix_fmt_descriptors[link->format];
 +    if (idet->csp->comp[0].depth_minus1 / 8 == 1)
 +        idet->filter_line = (void*)filter_line_c_16bit;
 +
 +    filter(ctx);
 +
 +    ff_draw_slice(ctx->outputs[0], 0, link->h, 1);
 +    ff_end_frame(ctx->outputs[0]);
 +}
 +
 +static int request_frame(AVFilterLink *link)
 +{
 +    AVFilterContext *ctx = link->src;
 +    IDETContext *idet = ctx->priv;
 +
 +    do {
 +        int ret;
 +
 +        if ((ret = ff_request_frame(link->src->inputs[0])))
 +            return ret;
 +    } while (!idet->cur);
 +
 +    return 0;
 +}
 +
 +static int poll_frame(AVFilterLink *link)
 +{
 +    IDETContext *idet = link->src->priv;
 +    int ret, val;
 +
 +    val = ff_poll_frame(link->src->inputs[0]);
 +
 +    if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape
 +        if ((ret = ff_request_frame(link->src->inputs[0])) < 0)
 +            return ret;
 +        val = ff_poll_frame(link->src->inputs[0]);
 +    }
 +    assert(idet->next || !val);
 +
 +    return val;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    IDETContext *idet = ctx->priv;
 +
 +    av_log(ctx, AV_LOG_INFO, "Single frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
 +           idet->prestat[TFF],
 +           idet->prestat[BFF],
 +           idet->prestat[PROGRSSIVE],
 +           idet->prestat[UNDETERMINED]
 +    );
 +    av_log(ctx, AV_LOG_INFO, "Multi frame detection: TFF:%d BFF:%d Progressive:%d Undetermined:%d\n",
 +           idet->poststat[TFF],
 +           idet->poststat[BFF],
 +           idet->poststat[PROGRSSIVE],
 +           idet->poststat[UNDETERMINED]
 +    );
 +
 +    if (idet->prev) avfilter_unref_buffer(idet->prev);
 +    if (idet->cur ) avfilter_unref_buffer(idet->cur );
 +    if (idet->next) avfilter_unref_buffer(idet->next);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    static const enum PixelFormat pix_fmts[] = {
 +        PIX_FMT_YUV420P,
 +        PIX_FMT_YUV422P,
 +        PIX_FMT_YUV444P,
 +        PIX_FMT_YUV410P,
 +        PIX_FMT_YUV411P,
 +        PIX_FMT_GRAY8,
 +        PIX_FMT_YUVJ420P,
 +        PIX_FMT_YUVJ422P,
 +        PIX_FMT_YUVJ444P,
 +        AV_NE( PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE ),
 +        PIX_FMT_YUV440P,
 +        PIX_FMT_YUVJ440P,
 +        AV_NE( PIX_FMT_YUV420P10BE, PIX_FMT_YUV420P10LE ),
 +        AV_NE( PIX_FMT_YUV422P10BE, PIX_FMT_YUV422P10LE ),
 +        AV_NE( PIX_FMT_YUV444P10BE, PIX_FMT_YUV444P10LE ),
 +        AV_NE( PIX_FMT_YUV420P16BE, PIX_FMT_YUV420P16LE ),
 +        AV_NE( PIX_FMT_YUV422P16BE, PIX_FMT_YUV422P16LE ),
 +        AV_NE( PIX_FMT_YUV444P16BE, PIX_FMT_YUV444P16LE ),
 +        PIX_FMT_YUVA420P,
 +        PIX_FMT_NONE
 +    };
 +
 +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
 +
 +    return 0;
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    IDETContext *idet = ctx->priv;
 +
 +    idet->csp = NULL;
 +
 +    idet->interlace_threshold   = 1.01;
 +    idet->progressive_threshold = 2.5;
 +
 +    if (args) sscanf(args, "%f:%f", &idet->interlace_threshold, &idet->progressive_threshold);
 +
 +    idet->last_type = UNDETERMINED;
 +    memset(idet->history, UNDETERMINED, HIST_SIZE);
 +
 +    idet->filter_line = filter_line_c;
 +
 +    return 0;
 +}
 +
 +static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
 +
 +AVFilter avfilter_vf_idet = {
 +    .name          = "idet",
 +    .description   = NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
 +
 +    .priv_size     = sizeof(IDETContext),
 +    .init          = init,
 +    .uninit        = uninit,
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name       = "default",
 +                                          .type             = AVMEDIA_TYPE_VIDEO,
 +                                          .start_frame      = start_frame,
 +                                          .draw_slice       = null_draw_slice,
 +                                          .end_frame        = end_frame,
 +                                          .rej_perms        = AV_PERM_REUSE2, },
 +                                        { .name = NULL}},
 +
 +    .outputs   = (const AVFilterPad[]) {{ .name       = "default",
 +                                          .type             = AVMEDIA_TYPE_VIDEO,
 +                                          .poll_frame       = poll_frame,
 +                                          .request_frame    = request_frame, },
 +                                        { .name = NULL}},
 +};
Simple merge
@@@ -91,9 -99,18 +91,9 @@@ static const AVOption lut_options[] = 
      {NULL},
  };
  
 -static const char *lut_get_name(void *ctx)
 -{
 -    return "lut";
 -}
 -
 -static const AVClass lut_class = {
 -    "LutContext",
 -    lut_get_name,
 -    lut_options
 -};
 +AVFILTER_DEFINE_CLASS(lut);
  
- static int init(AVFilterContext *ctx, const char *args, void *opaque)
+ static int init(AVFilterContext *ctx, const char *args)
  {
      LutContext *lut = ctx->priv;
      int ret;
index 43eecc3,0000000..0cf2b79
mode 100644,000000..100644
--- /dev/null
@@@ -1,911 -1,0 +1,911 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2011 Michael Niedermayer
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + *
 + * Parts of this file have been stolen from mplayer
 + */
 +
 +/**
 + * @file
 + */
 +
 +#include "avfilter.h"
 +#include "video.h"
 +#include "formats.h"
 +#include "internal.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/intreadwrite.h"
 +#include "libavutil/imgutils.h"
 +
 +#include "libmpcodecs/vf.h"
 +#include "libmpcodecs/img_format.h"
 +#include "libmpcodecs/cpudetect.h"
 +#include "libmpcodecs/vd_ffmpeg.h"
 +#include "libmpcodecs/vf_scale.h"
 +#include "libmpcodecs/libvo/fastmemcpy.h"
 +
 +#include "libswscale/swscale.h"
 +
 +
 +//FIXME maybe link the orig in
 +//XXX: identical pix_fmt must be following with each others
 +static const struct {
 +    int fmt;
 +    enum PixelFormat pix_fmt;
 +} conversion_map[] = {
 +    {IMGFMT_ARGB, PIX_FMT_ARGB},
 +    {IMGFMT_BGRA, PIX_FMT_BGRA},
 +    {IMGFMT_BGR24, PIX_FMT_BGR24},
 +    {IMGFMT_BGR16BE, PIX_FMT_RGB565BE},
 +    {IMGFMT_BGR16LE, PIX_FMT_RGB565LE},
 +    {IMGFMT_BGR15BE, PIX_FMT_RGB555BE},
 +    {IMGFMT_BGR15LE, PIX_FMT_RGB555LE},
 +    {IMGFMT_BGR12BE, PIX_FMT_RGB444BE},
 +    {IMGFMT_BGR12LE, PIX_FMT_RGB444LE},
 +    {IMGFMT_BGR8,  PIX_FMT_RGB8},
 +    {IMGFMT_BGR4,  PIX_FMT_RGB4},
 +    {IMGFMT_BGR1,  PIX_FMT_MONOBLACK},
 +    {IMGFMT_RGB1,  PIX_FMT_MONOBLACK},
 +    {IMGFMT_RG4B,  PIX_FMT_BGR4_BYTE},
 +    {IMGFMT_BG4B,  PIX_FMT_RGB4_BYTE},
 +    {IMGFMT_RGB48LE, PIX_FMT_RGB48LE},
 +    {IMGFMT_RGB48BE, PIX_FMT_RGB48BE},
 +    {IMGFMT_ABGR, PIX_FMT_ABGR},
 +    {IMGFMT_RGBA, PIX_FMT_RGBA},
 +    {IMGFMT_RGB24, PIX_FMT_RGB24},
 +    {IMGFMT_RGB16BE, PIX_FMT_BGR565BE},
 +    {IMGFMT_RGB16LE, PIX_FMT_BGR565LE},
 +    {IMGFMT_RGB15BE, PIX_FMT_BGR555BE},
 +    {IMGFMT_RGB15LE, PIX_FMT_BGR555LE},
 +    {IMGFMT_RGB12BE, PIX_FMT_BGR444BE},
 +    {IMGFMT_RGB12LE, PIX_FMT_BGR444LE},
 +    {IMGFMT_RGB8,  PIX_FMT_BGR8},
 +    {IMGFMT_RGB4,  PIX_FMT_BGR4},
 +    {IMGFMT_BGR8,  PIX_FMT_PAL8},
 +    {IMGFMT_YUY2,  PIX_FMT_YUYV422},
 +    {IMGFMT_UYVY,  PIX_FMT_UYVY422},
 +    {IMGFMT_NV12,  PIX_FMT_NV12},
 +    {IMGFMT_NV21,  PIX_FMT_NV21},
 +    {IMGFMT_Y800,  PIX_FMT_GRAY8},
 +    {IMGFMT_Y8,    PIX_FMT_GRAY8},
 +    {IMGFMT_YVU9,  PIX_FMT_YUV410P},
 +    {IMGFMT_IF09,  PIX_FMT_YUV410P},
 +    {IMGFMT_YV12,  PIX_FMT_YUV420P},
 +    {IMGFMT_I420,  PIX_FMT_YUV420P},
 +    {IMGFMT_IYUV,  PIX_FMT_YUV420P},
 +    {IMGFMT_411P,  PIX_FMT_YUV411P},
 +    {IMGFMT_422P,  PIX_FMT_YUV422P},
 +    {IMGFMT_444P,  PIX_FMT_YUV444P},
 +    {IMGFMT_440P,  PIX_FMT_YUV440P},
 +
 +    {IMGFMT_420A,  PIX_FMT_YUVA420P},
 +
 +    {IMGFMT_420P16_LE,  PIX_FMT_YUV420P16LE},
 +    {IMGFMT_420P16_BE,  PIX_FMT_YUV420P16BE},
 +    {IMGFMT_422P16_LE,  PIX_FMT_YUV422P16LE},
 +    {IMGFMT_422P16_BE,  PIX_FMT_YUV422P16BE},
 +    {IMGFMT_444P16_LE,  PIX_FMT_YUV444P16LE},
 +    {IMGFMT_444P16_BE,  PIX_FMT_YUV444P16BE},
 +
 +    // YUVJ are YUV formats that use the full Y range and not just
 +    // 16 - 235 (see colorspaces.txt).
 +    // Currently they are all treated the same way.
 +    {IMGFMT_YV12,  PIX_FMT_YUVJ420P},
 +    {IMGFMT_422P,  PIX_FMT_YUVJ422P},
 +    {IMGFMT_444P,  PIX_FMT_YUVJ444P},
 +    {IMGFMT_440P,  PIX_FMT_YUVJ440P},
 +
 +    {IMGFMT_XVMC_MOCO_MPEG2, PIX_FMT_XVMC_MPEG2_MC},
 +    {IMGFMT_XVMC_IDCT_MPEG2, PIX_FMT_XVMC_MPEG2_IDCT},
 +    {IMGFMT_VDPAU_MPEG1,     PIX_FMT_VDPAU_MPEG1},
 +    {IMGFMT_VDPAU_MPEG2,     PIX_FMT_VDPAU_MPEG2},
 +    {IMGFMT_VDPAU_H264,      PIX_FMT_VDPAU_H264},
 +    {IMGFMT_VDPAU_WMV3,      PIX_FMT_VDPAU_WMV3},
 +    {IMGFMT_VDPAU_VC1,       PIX_FMT_VDPAU_VC1},
 +    {IMGFMT_VDPAU_MPEG4,     PIX_FMT_VDPAU_MPEG4},
 +    {0, PIX_FMT_NONE}
 +};
 +
 +//copied from vf.c
 +extern const vf_info_t vf_info_1bpp;
 +extern const vf_info_t vf_info_ass;
 +extern const vf_info_t vf_info_bmovl;
 +extern const vf_info_t vf_info_crop;
 +extern const vf_info_t vf_info_decimate;
 +extern const vf_info_t vf_info_denoise3d;
 +extern const vf_info_t vf_info_detc;
 +extern const vf_info_t vf_info_dint;
 +extern const vf_info_t vf_info_divtc;
 +extern const vf_info_t vf_info_down3dright;
 +extern const vf_info_t vf_info_dsize;
 +extern const vf_info_t vf_info_dvbscale;
 +extern const vf_info_t vf_info_eq2;
 +extern const vf_info_t vf_info_eq;
 +extern const vf_info_t vf_info_expand;
 +extern const vf_info_t vf_info_field;
 +extern const vf_info_t vf_info_fil;
 +extern const vf_info_t vf_info_filmdint;
 +extern const vf_info_t vf_info_fixpts;
 +extern const vf_info_t vf_info_flip;
 +extern const vf_info_t vf_info_format;
 +extern const vf_info_t vf_info_framestep;
 +extern const vf_info_t vf_info_fspp;
 +extern const vf_info_t vf_info_geq;
 +extern const vf_info_t vf_info_halfpack;
 +extern const vf_info_t vf_info_harddup;
 +extern const vf_info_t vf_info_hqdn3d;
 +extern const vf_info_t vf_info_hue;
 +extern const vf_info_t vf_info_il;
 +extern const vf_info_t vf_info_ilpack;
 +extern const vf_info_t vf_info_ivtc;
 +extern const vf_info_t vf_info_kerndeint;
 +extern const vf_info_t vf_info_lavc;
 +extern const vf_info_t vf_info_lavcdeint;
 +extern const vf_info_t vf_info_mcdeint;
 +extern const vf_info_t vf_info_noformat;
 +extern const vf_info_t vf_info_noise;
 +extern const vf_info_t vf_info_ow;
 +extern const vf_info_t vf_info_palette;
 +extern const vf_info_t vf_info_perspective;
 +extern const vf_info_t vf_info_phase;
 +extern const vf_info_t vf_info_pp7;
 +extern const vf_info_t vf_info_pp;
 +extern const vf_info_t vf_info_pullup;
 +extern const vf_info_t vf_info_qp;
 +extern const vf_info_t vf_info_rectangle;
 +extern const vf_info_t vf_info_rotate;
 +extern const vf_info_t vf_info_sab;
 +extern const vf_info_t vf_info_scale;
 +extern const vf_info_t vf_info_smartblur;
 +extern const vf_info_t vf_info_softpulldown;
 +extern const vf_info_t vf_info_softskip;
 +extern const vf_info_t vf_info_spp;
 +extern const vf_info_t vf_info_stereo3d;
 +extern const vf_info_t vf_info_telecine;
 +extern const vf_info_t vf_info_test;
 +extern const vf_info_t vf_info_tfields;
 +extern const vf_info_t vf_info_tile;
 +extern const vf_info_t vf_info_tinterlace;
 +extern const vf_info_t vf_info_unsharp;
 +extern const vf_info_t vf_info_uspp;
 +extern const vf_info_t vf_info_vo;
 +extern const vf_info_t vf_info_yadif;
 +extern const vf_info_t vf_info_yuvcsp;
 +extern const vf_info_t vf_info_yvu9;
 +extern const vf_info_t vf_info_zrmjpeg;
 +
 +
 +static const vf_info_t* const filters[]={
 +    &vf_info_decimate,
 +    &vf_info_denoise3d,
 +    &vf_info_detc,
 +    &vf_info_dint,
 +    &vf_info_divtc,
 +    &vf_info_down3dright,
 +    &vf_info_dsize,
 +    &vf_info_eq2,
 +    &vf_info_eq,
 +    &vf_info_field,
 +    &vf_info_fil,
 +//    &vf_info_filmdint, cmmx.h vd.h ‘opt_screen_size_x’
 +    &vf_info_fixpts,
 +    &vf_info_framestep,
 +    &vf_info_fspp,
 +    &vf_info_geq,
 +    &vf_info_harddup,
 +    &vf_info_hqdn3d,
 +    &vf_info_hue,
 +    &vf_info_il,
 +    &vf_info_ilpack,
 +    &vf_info_ivtc,
 +    &vf_info_kerndeint,
 +    &vf_info_mcdeint,
 +    &vf_info_noise,
 +    &vf_info_ow,
 +    &vf_info_palette,
 +    &vf_info_perspective,
 +    &vf_info_phase,
 +    &vf_info_pp,
 +    &vf_info_pp7,
 +    &vf_info_pullup,
 +    &vf_info_qp,
 +    &vf_info_rectangle,
 +    &vf_info_rotate,
 +    &vf_info_sab,
 +    &vf_info_smartblur,
 +    &vf_info_softpulldown,
 +    &vf_info_softskip,
 +    &vf_info_spp,
 +    &vf_info_stereo3d,
 +    &vf_info_telecine,
 +    &vf_info_tile,
 +    &vf_info_tinterlace,
 +    &vf_info_unsharp,
 +    &vf_info_uspp,
 +    &vf_info_yuvcsp,
 +    &vf_info_yvu9,
 +
 +    NULL
 +};
 +
 +/*
 +Unsupported filters
 +1bpp
 +ass
 +bmovl
 +crop
 +dvbscale
 +flip
 +expand
 +format
 +halfpack
 +lavc
 +lavcdeint
 +noformat
 +pp
 +scale
 +tfields
 +vo
 +yadif
 +zrmjpeg
 +*/
 +
 +CpuCaps gCpuCaps; //FIXME initialize this so optims work
 +
 +
 +static void sws_getFlagsAndFilterFromCmdLine(int *flags, SwsFilter **srcFilterParam, SwsFilter **dstFilterParam)
 +{
 +        static int firstTime=1;
 +        *flags=0;
 +
 +#if ARCH_X86
 +        if(gCpuCaps.hasMMX)
 +                __asm__ volatile("emms\n\t"::: "memory"); //FIXME this should not be required but it IS (even for non-MMX versions)
 +#endif
 +        if(firstTime)
 +        {
 +                firstTime=0;
 +                *flags= SWS_PRINT_INFO;
 +        }
 +        else if( mp_msg_test(MSGT_VFILTER,MSGL_DBG2) ) *flags= SWS_PRINT_INFO;
 +
 +        switch(SWS_BILINEAR)
 +        {
 +                case 0: *flags|= SWS_FAST_BILINEAR; break;
 +                case 1: *flags|= SWS_BILINEAR; break;
 +                case 2: *flags|= SWS_BICUBIC; break;
 +                case 3: *flags|= SWS_X; break;
 +                case 4: *flags|= SWS_POINT; break;
 +                case 5: *flags|= SWS_AREA; break;
 +                case 6: *flags|= SWS_BICUBLIN; break;
 +                case 7: *flags|= SWS_GAUSS; break;
 +                case 8: *flags|= SWS_SINC; break;
 +                case 9: *flags|= SWS_LANCZOS; break;
 +                case 10:*flags|= SWS_SPLINE; break;
 +                default:*flags|= SWS_BILINEAR; break;
 +        }
 +
 +        *srcFilterParam= NULL;
 +        *dstFilterParam= NULL;
 +}
 +
 +//exact copy from vf_scale.c
 +// will use sws_flags & src_filter (from cmd line)
 +struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat)
 +{
 +        int flags, i;
 +        SwsFilter *dstFilterParam, *srcFilterParam;
 +        enum PixelFormat dfmt, sfmt;
 +
 +        for(i=0; conversion_map[i].fmt && dstFormat != conversion_map[i].fmt; i++);
 +        dfmt= conversion_map[i].pix_fmt;
 +        for(i=0; conversion_map[i].fmt && srcFormat != conversion_map[i].fmt; i++);
 +        sfmt= conversion_map[i].pix_fmt;
 +
 +        if (srcFormat == IMGFMT_RGB8 || srcFormat == IMGFMT_BGR8) sfmt = PIX_FMT_PAL8;
 +        sws_getFlagsAndFilterFromCmdLine(&flags, &srcFilterParam, &dstFilterParam);
 +
 +        return sws_getContext(srcW, srcH, sfmt, dstW, dstH, dfmt, flags , srcFilterParam, dstFilterParam, NULL);
 +}
 +
 +typedef struct {
 +    vf_instance_t vf;
 +    vf_instance_t next_vf;
 +    AVFilterContext *avfctx;
 +    int frame_returned;
 +} MPContext;
 +
 +void mp_msg(int mod, int lev, const char *format, ... ){
 +    va_list va;
 +    va_start(va, format);
 +    //FIXME convert lev/mod
 +    av_vlog(NULL, AV_LOG_DEBUG, format, va);
 +    va_end(va);
 +}
 +
 +int mp_msg_test(int mod, int lev){
 +    return 123;
 +}
 +
 +void init_avcodec(void)
 +{
 +    //we maybe should init but its kinda 1. unneeded 2. a bit inpolite from here
 +}
 +
 +//Exact copy of vf.c
 +void vf_clone_mpi_attributes(mp_image_t* dst, mp_image_t* src){
 +    dst->pict_type= src->pict_type;
 +    dst->fields = src->fields;
 +    dst->qscale_type= src->qscale_type;
 +    if(dst->width == src->width && dst->height == src->height){
 +        dst->qstride= src->qstride;
 +        dst->qscale= src->qscale;
 +    }
 +}
 +
 +//Exact copy of vf.c
 +void vf_next_draw_slice(struct vf_instance *vf,unsigned char** src, int * stride,int w, int h, int x, int y){
 +    if (vf->next->draw_slice) {
 +        vf->next->draw_slice(vf->next,src,stride,w,h,x,y);
 +        return;
 +    }
 +    if (!vf->dmpi) {
 +        mp_msg(MSGT_VFILTER,MSGL_ERR,"draw_slice: dmpi not stored by vf_%s\n", vf->info->name);
 +        return;
 +    }
 +    if (!(vf->dmpi->flags & MP_IMGFLAG_PLANAR)) {
 +        memcpy_pic(vf->dmpi->planes[0]+y*vf->dmpi->stride[0]+vf->dmpi->bpp/8*x,
 +            src[0], vf->dmpi->bpp/8*w, h, vf->dmpi->stride[0], stride[0]);
 +        return;
 +    }
 +    memcpy_pic(vf->dmpi->planes[0]+y*vf->dmpi->stride[0]+x, src[0],
 +        w, h, vf->dmpi->stride[0], stride[0]);
 +    memcpy_pic(vf->dmpi->planes[1]+(y>>vf->dmpi->chroma_y_shift)*vf->dmpi->stride[1]+(x>>vf->dmpi->chroma_x_shift),
 +        src[1], w>>vf->dmpi->chroma_x_shift, h>>vf->dmpi->chroma_y_shift, vf->dmpi->stride[1], stride[1]);
 +    memcpy_pic(vf->dmpi->planes[2]+(y>>vf->dmpi->chroma_y_shift)*vf->dmpi->stride[2]+(x>>vf->dmpi->chroma_x_shift),
 +        src[2], w>>vf->dmpi->chroma_x_shift, h>>vf->dmpi->chroma_y_shift, vf->dmpi->stride[2], stride[2]);
 +}
 +
 +//Exact copy of vf.c
 +void vf_mpi_clear(mp_image_t* mpi,int x0,int y0,int w,int h){
 +    int y;
 +    if(mpi->flags&MP_IMGFLAG_PLANAR){
 +        y0&=~1;h+=h&1;
 +        if(x0==0 && w==mpi->width){
 +            // full width clear:
 +            memset(mpi->planes[0]+mpi->stride[0]*y0,0,mpi->stride[0]*h);
 +            memset(mpi->planes[1]+mpi->stride[1]*(y0>>mpi->chroma_y_shift),128,mpi->stride[1]*(h>>mpi->chroma_y_shift));
 +            memset(mpi->planes[2]+mpi->stride[2]*(y0>>mpi->chroma_y_shift),128,mpi->stride[2]*(h>>mpi->chroma_y_shift));
 +        } else
 +        for(y=y0;y<y0+h;y+=2){
 +            memset(mpi->planes[0]+x0+mpi->stride[0]*y,0,w);
 +            memset(mpi->planes[0]+x0+mpi->stride[0]*(y+1),0,w);
 +            memset(mpi->planes[1]+(x0>>mpi->chroma_x_shift)+mpi->stride[1]*(y>>mpi->chroma_y_shift),128,(w>>mpi->chroma_x_shift));
 +            memset(mpi->planes[2]+(x0>>mpi->chroma_x_shift)+mpi->stride[2]*(y>>mpi->chroma_y_shift),128,(w>>mpi->chroma_x_shift));
 +        }
 +        return;
 +    }
 +    // packed:
 +    for(y=y0;y<y0+h;y++){
 +        unsigned char* dst=mpi->planes[0]+mpi->stride[0]*y+(mpi->bpp>>3)*x0;
 +        if(mpi->flags&MP_IMGFLAG_YUV){
 +            unsigned int* p=(unsigned int*) dst;
 +            int size=(mpi->bpp>>3)*w/4;
 +            int i;
 +#if HAVE_BIGENDIAN
 +#define CLEAR_PACKEDYUV_PATTERN 0x00800080
 +#define CLEAR_PACKEDYUV_PATTERN_SWAPPED 0x80008000
 +#else
 +#define CLEAR_PACKEDYUV_PATTERN 0x80008000
 +#define CLEAR_PACKEDYUV_PATTERN_SWAPPED 0x00800080
 +#endif
 +            if(mpi->flags&MP_IMGFLAG_SWAPPED){
 +                for(i=0;i<size-3;i+=4) p[i]=p[i+1]=p[i+2]=p[i+3]=CLEAR_PACKEDYUV_PATTERN_SWAPPED;
 +                for(;i<size;i++) p[i]=CLEAR_PACKEDYUV_PATTERN_SWAPPED;
 +            } else {
 +                for(i=0;i<size-3;i+=4) p[i]=p[i+1]=p[i+2]=p[i+3]=CLEAR_PACKEDYUV_PATTERN;
 +                for(;i<size;i++) p[i]=CLEAR_PACKEDYUV_PATTERN;
 +            }
 +        } else
 +            memset(dst,0,(mpi->bpp>>3)*w);
 +    }
 +}
 +
 +int vf_next_query_format(struct vf_instance *vf, unsigned int fmt){
 +    return 1;
 +}
 +
 +//used by delogo
 +unsigned int vf_match_csp(vf_instance_t** vfp,const unsigned int* list,unsigned int preferred){
 +    return preferred;
 +}
 +
 +mp_image_t* vf_get_image(vf_instance_t* vf, unsigned int outfmt, int mp_imgtype, int mp_imgflag, int w, int h){
 +    MPContext *m= (MPContext*)(((uint8_t*)vf) - offsetof(MPContext, next_vf));
 +  mp_image_t* mpi=NULL;
 +  int w2;
 +  int number = mp_imgtype >> 16;
 +
 +  av_assert0(vf->next == NULL); // all existing filters call this just on next
 +
 +  //vf_dint needs these as it calls vf_get_image() before configuring the output
 +  if(vf->w==0 && w>0) vf->w=w;
 +  if(vf->h==0 && h>0) vf->h=h;
 +
 +  av_assert0(w == -1 || w >= vf->w);
 +  av_assert0(h == -1 || h >= vf->h);
 +  av_assert0(vf->w > 0);
 +  av_assert0(vf->h > 0);
 +
 +  av_log(m->avfctx, AV_LOG_DEBUG, "get_image: %d:%d, vf: %d:%d\n", w,h,vf->w,vf->h);
 +
 +  if (w == -1) w = vf->w;
 +  if (h == -1) h = vf->h;
 +
 +  w2=(mp_imgflag&MP_IMGFLAG_ACCEPT_ALIGNED_STRIDE)?((w+15)&(~15)):w;
 +
 +  // Note: we should call libvo first to check if it supports direct rendering
 +  // and if not, then fallback to software buffers:
 +  switch(mp_imgtype & 0xff){
 +  case MP_IMGTYPE_EXPORT:
 +    if(!vf->imgctx.export_images[0]) vf->imgctx.export_images[0]=new_mp_image(w2,h);
 +    mpi=vf->imgctx.export_images[0];
 +    break;
 +  case MP_IMGTYPE_STATIC:
 +    if(!vf->imgctx.static_images[0]) vf->imgctx.static_images[0]=new_mp_image(w2,h);
 +    mpi=vf->imgctx.static_images[0];
 +    break;
 +  case MP_IMGTYPE_TEMP:
 +    if(!vf->imgctx.temp_images[0]) vf->imgctx.temp_images[0]=new_mp_image(w2,h);
 +    mpi=vf->imgctx.temp_images[0];
 +    break;
 +  case MP_IMGTYPE_IPB:
 +    if(!(mp_imgflag&MP_IMGFLAG_READABLE)){ // B frame:
 +      if(!vf->imgctx.temp_images[0]) vf->imgctx.temp_images[0]=new_mp_image(w2,h);
 +      mpi=vf->imgctx.temp_images[0];
 +      break;
 +    }
 +  case MP_IMGTYPE_IP:
 +    if(!vf->imgctx.static_images[vf->imgctx.static_idx]) vf->imgctx.static_images[vf->imgctx.static_idx]=new_mp_image(w2,h);
 +    mpi=vf->imgctx.static_images[vf->imgctx.static_idx];
 +    vf->imgctx.static_idx^=1;
 +    break;
 +  case MP_IMGTYPE_NUMBERED:
 +    if (number == -1) {
 +      int i;
 +      for (i = 0; i < NUM_NUMBERED_MPI; i++)
 +        if (!vf->imgctx.numbered_images[i] || !vf->imgctx.numbered_images[i]->usage_count)
 +          break;
 +      number = i;
 +    }
 +    if (number < 0 || number >= NUM_NUMBERED_MPI) return NULL;
 +    if (!vf->imgctx.numbered_images[number]) vf->imgctx.numbered_images[number] = new_mp_image(w2,h);
 +    mpi = vf->imgctx.numbered_images[number];
 +    mpi->number = number;
 +    break;
 +  }
 +  if(mpi){
 +    mpi->type=mp_imgtype;
 +    mpi->w=vf->w; mpi->h=vf->h;
 +    // keep buffer allocation status & color flags only:
 +//    mpi->flags&=~(MP_IMGFLAG_PRESERVE|MP_IMGFLAG_READABLE|MP_IMGFLAG_DIRECT);
 +    mpi->flags&=MP_IMGFLAG_ALLOCATED|MP_IMGFLAG_TYPE_DISPLAYED|MP_IMGFLAGMASK_COLORS;
 +    // accept restrictions, draw_slice and palette flags only:
 +    mpi->flags|=mp_imgflag&(MP_IMGFLAGMASK_RESTRICTIONS|MP_IMGFLAG_DRAW_CALLBACK|MP_IMGFLAG_RGB_PALETTE);
 +    if(!vf->draw_slice) mpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
 +    if(mpi->width!=w2 || mpi->height!=h){
 +//      printf("vf.c: MPI parameters changed!  %dx%d -> %dx%d   \n", mpi->width,mpi->height,w2,h);
 +        if(mpi->flags&MP_IMGFLAG_ALLOCATED){
 +            if(mpi->width<w2 || mpi->height<h){
 +                // need to re-allocate buffer memory:
 +                av_free(mpi->planes[0]);
 +                mpi->flags&=~MP_IMGFLAG_ALLOCATED;
 +                mp_msg(MSGT_VFILTER,MSGL_V,"vf.c: have to REALLOCATE buffer memory :(\n");
 +            }
 +//      } else {
 +        } {
 +            mpi->width=w2; mpi->chroma_width=(w2 + (1<<mpi->chroma_x_shift) - 1)>>mpi->chroma_x_shift;
 +            mpi->height=h; mpi->chroma_height=(h + (1<<mpi->chroma_y_shift) - 1)>>mpi->chroma_y_shift;
 +        }
 +    }
 +    if(!mpi->bpp) mp_image_setfmt(mpi,outfmt);
 +    if(!(mpi->flags&MP_IMGFLAG_ALLOCATED) && mpi->type>MP_IMGTYPE_EXPORT){
 +
 +        av_assert0(!vf->get_image);
 +        // check libvo first!
 +        if(vf->get_image) vf->get_image(vf,mpi);
 +
 +        if(!(mpi->flags&MP_IMGFLAG_DIRECT)){
 +          // non-direct and not yet allocated image. allocate it!
 +          if (!mpi->bpp) { // no way we can allocate this
 +              mp_msg(MSGT_DECVIDEO, MSGL_FATAL,
 +                     "vf_get_image: Tried to allocate a format that can not be allocated!\n");
 +              return NULL;
 +          }
 +
 +          // check if codec prefer aligned stride:
 +          if(mp_imgflag&MP_IMGFLAG_PREFER_ALIGNED_STRIDE){
 +              int align=(mpi->flags&MP_IMGFLAG_PLANAR &&
 +                         mpi->flags&MP_IMGFLAG_YUV) ?
 +                         (8<<mpi->chroma_x_shift)-1 : 15; // -- maybe FIXME
 +              w2=((w+align)&(~align));
 +              if(mpi->width!=w2){
 +#if 0
 +                  // we have to change width... check if we CAN co it:
 +                  int flags=vf->query_format(vf,outfmt); // should not fail
 +                  if(!(flags&3)) mp_msg(MSGT_DECVIDEO,MSGL_WARN,"??? vf_get_image{vf->query_format(outfmt)} failed!\n");
 +//                printf("query -> 0x%X    \n",flags);
 +                  if(flags&VFCAP_ACCEPT_STRIDE){
 +#endif
 +                      mpi->width=w2;
 +                      mpi->chroma_width=(w2 + (1<<mpi->chroma_x_shift) - 1)>>mpi->chroma_x_shift;
 +//                  }
 +              }
 +          }
 +
 +          mp_image_alloc_planes(mpi);
 +//        printf("clearing img!\n");
 +          vf_mpi_clear(mpi,0,0,mpi->width,mpi->height);
 +        }
 +    }
 +    av_assert0(!vf->start_slice);
 +    if(mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)
 +        if(vf->start_slice) vf->start_slice(vf,mpi);
 +    if(!(mpi->flags&MP_IMGFLAG_TYPE_DISPLAYED)){
 +            mp_msg(MSGT_DECVIDEO,MSGL_V,"*** [%s] %s%s mp_image_t, %dx%dx%dbpp %s %s, %d bytes\n",
 +                  "NULL"/*vf->info->name*/,
 +                  (mpi->type==MP_IMGTYPE_EXPORT)?"Exporting":
 +                  ((mpi->flags&MP_IMGFLAG_DIRECT)?"Direct Rendering":"Allocating"),
 +                  (mpi->flags&MP_IMGFLAG_DRAW_CALLBACK)?" (slices)":"",
 +                  mpi->width,mpi->height,mpi->bpp,
 +                  (mpi->flags&MP_IMGFLAG_YUV)?"YUV":((mpi->flags&MP_IMGFLAG_SWAPPED)?"BGR":"RGB"),
 +                  (mpi->flags&MP_IMGFLAG_PLANAR)?"planar":"packed",
 +                  mpi->bpp*mpi->width*mpi->height/8);
 +            mp_msg(MSGT_DECVIDEO,MSGL_DBG2,"(imgfmt: %x, planes: %p,%p,%p strides: %d,%d,%d, chroma: %dx%d, shift: h:%d,v:%d)\n",
 +                mpi->imgfmt, mpi->planes[0], mpi->planes[1], mpi->planes[2],
 +                mpi->stride[0], mpi->stride[1], mpi->stride[2],
 +                mpi->chroma_width, mpi->chroma_height, mpi->chroma_x_shift, mpi->chroma_y_shift);
 +            mpi->flags|=MP_IMGFLAG_TYPE_DISPLAYED;
 +    }
 +
 +  mpi->qscale = NULL;
 +  }
 +  mpi->usage_count++;
 +//    printf("\rVF_MPI: %p %p %p %d %d %d    \n",
 +//      mpi->planes[0],mpi->planes[1],mpi->planes[2],
 +//      mpi->stride[0],mpi->stride[1],mpi->stride[2]);
 +  return mpi;
 +}
 +
 +
 +int vf_next_put_image(struct vf_instance *vf,mp_image_t *mpi, double pts){
 +    MPContext *m= (void*)vf;
 +    AVFilterLink *outlink     = m->avfctx->outputs[0];
 +    AVFilterBuffer    *pic    = av_mallocz(sizeof(AVFilterBuffer));
 +    AVFilterBufferRef *picref = av_mallocz(sizeof(AVFilterBufferRef));
 +    int i;
 +
 +    av_assert0(vf->next);
 +
 +    av_log(m->avfctx, AV_LOG_DEBUG, "vf_next_put_image\n");
 +
 +    if (!pic || !picref)
 +        goto fail;
 +
 +    picref->buf = pic;
 +    picref->buf->please_use_av_free= (void*)av_free;
 +    if (!(picref->video = av_mallocz(sizeof(AVFilterBufferRefVideoProps))))
 +        goto fail;
 +
 +    pic->w = picref->video->w = mpi->w;
 +    pic->h = picref->video->h = mpi->h;
 +
 +    /* make sure the buffer gets read permission or it's useless for output */
 +    picref->perms = AV_PERM_READ | AV_PERM_REUSE2;
 +//    av_assert0(mpi->flags&MP_IMGFLAG_READABLE);
 +    if(!(mpi->flags&MP_IMGFLAG_PRESERVE))
 +        picref->perms |= AV_PERM_WRITE;
 +
 +    pic->refcount = 1;
 +    picref->type = AVMEDIA_TYPE_VIDEO;
 +
 +    for(i=0; conversion_map[i].fmt && mpi->imgfmt != conversion_map[i].fmt; i++);
 +    pic->format = picref->format = conversion_map[i].pix_fmt;
 +
 +    memcpy(pic->data,        mpi->planes,   FFMIN(sizeof(pic->data)    , sizeof(mpi->planes)));
 +    memcpy(pic->linesize,    mpi->stride,   FFMIN(sizeof(pic->linesize), sizeof(mpi->stride)));
 +    memcpy(picref->data,     pic->data,     sizeof(picref->data));
 +    memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize));
 +
 +    if(pts != MP_NOPTS_VALUE)
 +        picref->pts= pts * av_q2d(outlink->time_base);
 +
 +    ff_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
 +    ff_draw_slice(outlink, 0, picref->video->h, 1);
 +    ff_end_frame(outlink);
 +    avfilter_unref_buffer(picref);
 +    m->frame_returned++;
 +
 +    return 1;
 +fail:
 +    if (picref && picref->video)
 +        av_free(picref->video);
 +    av_free(picref);
 +    av_free(pic);
 +    return 0;
 +}
 +
 +int vf_next_config(struct vf_instance *vf,
 +        int width, int height, int d_width, int d_height,
 +        unsigned int voflags, unsigned int outfmt){
 +
 +    av_assert0(width>0 && height>0);
 +    vf->next->w = width; vf->next->h = height;
 +
 +    return 1;
 +#if 0
 +    int flags=vf->next->query_format(vf->next,outfmt);
 +    if(!flags){
 +        // hmm. colorspace mismatch!!!
 +        //this is fatal for us ATM
 +        return 0;
 +    }
 +    mp_msg(MSGT_VFILTER,MSGL_V,"REQ: flags=0x%X  req=0x%X  \n",flags,vf->default_reqs);
 +    miss=vf->default_reqs - (flags&vf->default_reqs);
 +    if(miss&VFCAP_ACCEPT_STRIDE){
 +        // vf requires stride support but vf->next doesn't support it!
 +        // let's insert the 'expand' filter, it does the job for us:
 +        vf_instance_t* vf2=vf_open_filter(vf->next,"expand",NULL);
 +        if(!vf2) return 0; // shouldn't happen!
 +        vf->next=vf2;
 +    }
 +    vf->next->w = width; vf->next->h = height;
 +#endif
 +    return 1;
 +}
 +
 +int vf_next_control(struct vf_instance *vf, int request, void* data){
 +    MPContext *m= (void*)vf;
 +    av_log(m->avfctx, AV_LOG_DEBUG, "Received control %d\n", request);
 +    return 0;
 +}
 +
 +static int vf_default_query_format(struct vf_instance *vf, unsigned int fmt){
 +    MPContext *m= (void*)vf;
 +    int i;
 +    av_log(m->avfctx, AV_LOG_DEBUG, "query %X\n", fmt);
 +
 +    for(i=0; conversion_map[i].fmt; i++){
 +        if(fmt==conversion_map[i].fmt)
 +            return 1; //we suport all
 +    }
 +    return 0;
 +}
 +
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    MPContext *m = ctx->priv;
 +    char name[256];
 +    int i;
 +
 +    m->avfctx= ctx;
 +
 +    if(!args || 1!=sscanf(args, "%255[^:=]", name)){
 +        av_log(ctx, AV_LOG_ERROR, "Invalid parameter.\n");
 +        return AVERROR(EINVAL);
 +    }
 +    args+= strlen(name)+1;
 +
 +    for(i=0; ;i++){
 +        if(!filters[i] || !strcmp(name, filters[i]->name))
 +            break;
 +    }
 +
 +    if(!filters[i]){
 +        av_log(ctx, AV_LOG_ERROR, "Unknown filter %s\n", name);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    av_log(ctx, AV_LOG_WARNING,
 +           "'%s' is a wrapped MPlayer filter (libmpcodecs). This filter may be removed\n"
 +           "once it has been ported to a native libavfilter.\n", name);
 +
 +    memset(&m->vf,0,sizeof(m->vf));
 +    m->vf.info= filters[i];
 +
 +    m->vf.next        = &m->next_vf;
 +    m->vf.put_image   = vf_next_put_image;
 +    m->vf.config      = vf_next_config;
 +    m->vf.query_format= vf_default_query_format;
 +    m->vf.control     = vf_next_control;
 +    m->vf.default_caps=VFCAP_ACCEPT_STRIDE;
 +    m->vf.default_reqs=0;
 +    if(m->vf.info->opts)
 +        av_log(ctx, AV_LOG_ERROR, "opts / m_struct_set is unsupported\n");
 +#if 0
 +    if(vf->info->opts) { // vf_vo get some special argument
 +      const m_struct_t* st = vf->info->opts;
 +      void* vf_priv = m_struct_alloc(st);
 +      int n;
 +      for(n = 0 ; args && args[2*n] ; n++)
 +        m_struct_set(st,vf_priv,args[2*n],args[2*n+1]);
 +      vf->priv = vf_priv;
 +      args = NULL;
 +    } else // Otherwise we should have the '_oldargs_'
 +      if(args && !strcmp(args[0],"_oldargs_"))
 +        args = (char**)args[1];
 +      else
 +        args = NULL;
 +#endif
 +    if(m->vf.info->vf_open(&m->vf, args)<=0){
 +        av_log(ctx, AV_LOG_ERROR, "vf_open() of %s with arg=%s failed\n", name, args);
 +        return -1;
 +    }
 +
 +    return 0;
 +}
 +
 +static av_cold void uninit(AVFilterContext *ctx)
 +{
 +    MPContext *m = ctx->priv;
 +    vf_instance_t *vf = &m->vf;
 +
 +    while(vf){
 +        vf_instance_t *next = vf->next;
 +        if(vf->uninit)
 +            vf->uninit(vf);
 +        free_mp_image(vf->imgctx.static_images[0]);
 +        free_mp_image(vf->imgctx.static_images[1]);
 +        free_mp_image(vf->imgctx.temp_images[0]);
 +        free_mp_image(vf->imgctx.export_images[0]);
 +        vf = next;
 +    }
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    AVFilterFormats *avfmts=NULL;
 +    MPContext *m = ctx->priv;
 +    enum PixelFormat lastpixfmt = PIX_FMT_NONE;
 +    int i;
 +
 +    for(i=0; conversion_map[i].fmt; i++){
 +        av_log(ctx, AV_LOG_DEBUG, "query: %X\n", conversion_map[i].fmt);
 +        if(m->vf.query_format(&m->vf, conversion_map[i].fmt)){
 +            av_log(ctx, AV_LOG_DEBUG, "supported,adding\n");
 +            if (conversion_map[i].pix_fmt != lastpixfmt) {
 +                ff_add_format(&avfmts, conversion_map[i].pix_fmt);
 +                lastpixfmt = conversion_map[i].pix_fmt;
 +            }
 +        }
 +    }
 +
 +    //We assume all allowed input formats are also allowed output formats
 +    ff_set_common_formats(ctx, avfmts);
 +    return 0;
 +}
 +
 +static int config_inprops(AVFilterLink *inlink)
 +{
 +    MPContext *m = inlink->dst->priv;
 +    int i;
 +    for(i=0; conversion_map[i].fmt && conversion_map[i].pix_fmt != inlink->format; i++);
 +
 +    av_assert0(conversion_map[i].fmt && inlink->w && inlink->h);
 +
 +    m->vf.fmt.have_configured = 1;
 +    m->vf.fmt.orig_height     = inlink->h;
 +    m->vf.fmt.orig_width      = inlink->w;
 +    m->vf.fmt.orig_fmt        = conversion_map[i].fmt;
 +
 +    if(m->vf.config(&m->vf, inlink->w, inlink->h, inlink->w, inlink->h, 0, conversion_map[i].fmt)<=0)
 +        return -1;
 +
 +    return 0;
 +}
 +
 +static int config_outprops(AVFilterLink *outlink)
 +{
 +    MPContext *m = outlink->src->priv;
 +
 +    outlink->w = m->next_vf.w;
 +    outlink->h = m->next_vf.h;
 +
 +    return 0;
 +}
 +
 +static int request_frame(AVFilterLink *outlink)
 +{
 +    MPContext *m = outlink->src->priv;
 +    int ret;
 +
 +    av_log(m->avfctx, AV_LOG_DEBUG, "mp request_frame\n");
 +
 +    for(m->frame_returned=0; !m->frame_returned;){
 +        ret=ff_request_frame(outlink->src->inputs[0]);
 +        if(ret<0)
 +            break;
 +    }
 +
 +    av_log(m->avfctx, AV_LOG_DEBUG, "mp request_frame ret=%d\n", ret);
 +    return ret;
 +}
 +
 +static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
 +{
 +}
 +
 +static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
 +{
 +}
 +
 +static void end_frame(AVFilterLink *inlink)
 +{
 +    MPContext *m = inlink->dst->priv;
 +    AVFilterBufferRef *inpic  = inlink->cur_buf;
 +    int i;
 +    double pts= MP_NOPTS_VALUE;
 +    mp_image_t* mpi = new_mp_image(inpic->video->w, inpic->video->h);
 +
 +    if(inpic->pts != AV_NOPTS_VALUE)
 +        pts= inpic->pts / av_q2d(inlink->time_base);
 +
 +    for(i=0; conversion_map[i].fmt && conversion_map[i].pix_fmt != inlink->format; i++);
 +    mp_image_setfmt(mpi,conversion_map[i].fmt);
 +
 +    memcpy(mpi->planes, inpic->data,     FFMIN(sizeof(inpic->data)    , sizeof(mpi->planes)));
 +    memcpy(mpi->stride, inpic->linesize, FFMIN(sizeof(inpic->linesize), sizeof(mpi->stride)));
 +
 +    //FIXME pass interleced & tff flags around
 +
 +    // mpi->flags|=MP_IMGFLAG_ALLOCATED; ?
 +    mpi->flags |= MP_IMGFLAG_READABLE;
 +    if(!(inpic->perms & AV_PERM_WRITE))
 +        mpi->flags |= MP_IMGFLAG_PRESERVE;
 +    if(m->vf.put_image(&m->vf, mpi, pts) == 0){
 +        av_log(m->avfctx, AV_LOG_DEBUG, "put_image() says skip\n");
 +    }
 +    free_mp_image(mpi);
 +
 +    avfilter_unref_buffer(inpic);
 +}
 +
 +AVFilter avfilter_vf_mp = {
 +    .name      = "mp",
 +    .description = NULL_IF_CONFIG_SMALL("Apply a libmpcodecs filter to the input video."),
 +    .init = init,
 +    .uninit = uninit,
 +    .priv_size = sizeof(MPContext),
 +    .query_formats = query_formats,
 +
 +    .inputs    = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .type            = AVMEDIA_TYPE_VIDEO,
 +                                    .start_frame     = start_frame,
 +                                    .draw_slice      = null_draw_slice,
 +                                    .end_frame       = end_frame,
 +                                    .config_props    = config_inprops,
 +                                    .min_perms       = AV_PERM_READ, },
 +                                  { .name = NULL}},
 +    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
 +                                    .type            = AVMEDIA_TYPE_VIDEO,
 +                                    .request_frame   = request_frame,
 +                                    .config_props    = config_outprops, },
 +                                  { .name = NULL}},
 +};
@@@ -60,82 -60,28 +60,82 @@@ enum var_name 
  #define MAIN    0
  #define OVERLAY 1
  
 +#define R 0
 +#define G 1
 +#define B 2
 +#define A 3
 +
 +#define Y 0
 +#define U 1
 +#define V 2
 +
  typedef struct {
 +    const AVClass *class;
      int x, y;                   ///< position of overlayed picture
  
 +    int allow_packed_rgb;
 +    uint8_t frame_requested;
 +    uint8_t overlay_eof;
 +    uint8_t main_is_packed_rgb;
 +    uint8_t main_rgba_map[4];
 +    uint8_t main_has_alpha;
 +    uint8_t overlay_is_packed_rgb;
 +    uint8_t overlay_rgba_map[4];
 +    uint8_t overlay_has_alpha;
 +
      AVFilterBufferRef *overpicref;
 +    struct FFBufQueue queue_main;
 +    struct FFBufQueue queue_over;
  
 -    int max_plane_step[4];      ///< steps per pixel for each plane
 +    int main_pix_step[4];       ///< steps per pixel for each plane of the main output
 +    int overlay_pix_step[4];    ///< steps per pixel for each plane of the overlay
      int hsub, vsub;             ///< chroma subsampling values
  
 -    char x_expr[256], y_expr[256];
 +    char *x_expr, *y_expr;
  } OverlayContext;
  
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +#define OFFSET(x) offsetof(OverlayContext, x)
 +
 +static const AVOption overlay_options[] = {
 +    { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX },
 +    { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX },
 +    {"rgb", "force packed RGB in input and output", OFFSET(allow_packed_rgb), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
 +    {NULL},
 +};
 +
 +AVFILTER_DEFINE_CLASS(overlay);
 +
+ static av_cold int init(AVFilterContext *ctx, const char *args)
  {
      OverlayContext *over = ctx->priv;
 +    char *args1 = av_strdup(args);
 +    char *expr, *bufptr = NULL;
 +    int ret = 0;
 +
 +    over->class = &overlay_class;
 +    av_opt_set_defaults(over);
 +
 +    if (expr = av_strtok(args1, ":", &bufptr)) {
 +        av_free(over->x_expr);
 +        if (!(over->x_expr = av_strdup(expr))) {
 +            ret = AVERROR(ENOMEM);
 +            goto end;
 +        }
 +    }
 +    if (expr = av_strtok(NULL, ":", &bufptr)) {
 +        av_free(over->y_expr);
 +        if (!(over->y_expr = av_strdup(expr))) {
 +            ret = AVERROR(ENOMEM);
 +            goto end;
 +        }
 +    }
  
 -    av_strlcpy(over->x_expr, "0", sizeof(over->x_expr));
 -    av_strlcpy(over->y_expr, "0", sizeof(over->y_expr));
 -
 -    if (args)
 -        sscanf(args, "%255[^:]:%255[^:]", over->x_expr, over->y_expr);
 +    if (bufptr && (ret = av_set_options_string(over, bufptr, "=", ":")) < 0)
 +        goto end;
  
 -    return 0;
 +end:
 +    av_free(args1);
 +    return ret;
  }
  
  static av_cold void uninit(AVFilterContext *ctx)
Simple merge
index c912fc3,0000000..fdfe5bd
mode 100644,000000..100644
--- /dev/null
@@@ -1,573 -1,0 +1,573 @@@
- static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 +/*
 + * Copyright (c) 2005 Robert Edele <yartrebo@earthlink.net>
 + * Copyright (c) 2012 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * Advanced blur-based logo removing filter
 + *
 + * This filter loads an image mask file showing where a logo is and
 + * uses a blur transform to remove the logo.
 + *
 + * Based on the libmpcodecs remove-logo filter by Robert Edele.
 + */
 +
 +/**
 + * This code implements a filter to remove annoying TV logos and other annoying
 + * images placed onto a video stream. It works by filling in the pixels that
 + * comprise the logo with neighboring pixels. The transform is very loosely
 + * based on a gaussian blur, but it is different enough to merit its own
 + * paragraph later on. It is a major improvement on the old delogo filter as it
 + * both uses a better blurring algorithm and uses a bitmap to use an arbitrary
 + * and generally much tighter fitting shape than a rectangle.
 + *
 + * The logo removal algorithm has two key points. The first is that it
 + * distinguishes between pixels in the logo and those not in the logo by using
 + * the passed-in bitmap. Pixels not in the logo are copied over directly without
 + * being modified and they also serve as source pixels for the logo
 + * fill-in. Pixels inside the logo have the mask applied.
 + *
 + * At init-time the bitmap is reprocessed internally, and the distance to the
 + * nearest edge of the logo (Manhattan distance), along with a little extra to
 + * remove rough edges, is stored in each pixel. This is done using an in-place
 + * erosion algorithm, and incrementing each pixel that survives any given
 + * erosion.  Once every pixel is eroded, the maximum value is recorded, and a
 + * set of masks from size 0 to this size are generaged. The masks are circular
 + * binary masks, where each pixel within a radius N (where N is the size of the
 + * mask) is a 1, and all other pixels are a 0. Although a gaussian mask would be
 + * more mathematically accurate, a binary mask works better in practice because
 + * we generally do not use the central pixels in the mask (because they are in
 + * the logo region), and thus a gaussian mask will cause too little blur and
 + * thus a very unstable image.
 + *
 + * The mask is applied in a special way. Namely, only pixels in the mask that
 + * line up to pixels outside the logo are used. The dynamic mask size means that
 + * the mask is just big enough so that the edges touch pixels outside the logo,
 + * so the blurring is kept to a minimum and at least the first boundary
 + * condition is met (that the image function itself is continuous), even if the
 + * second boundary condition (that the derivative of the image function is
 + * continuous) is not met. A masking algorithm that does preserve the second
 + * boundary coundition (perhaps something based on a highly-modified bi-cubic
 + * algorithm) should offer even better results on paper, but the noise in a
 + * typical TV signal should make anything based on derivatives hopelessly noisy.
 + */
 +
 +#include "libavutil/imgutils.h"
 +#include "avfilter.h"
 +#include "formats.h"
 +#include "video.h"
 +#include "bbox.h"
 +#include "lavfutils.h"
 +#include "lswsutils.h"
 +
 +typedef struct {
 +    /* Stores our collection of masks. The first is for an array of
 +       the second for the y axis, and the third for the x axis. */
 +    int ***mask;
 +    int max_mask_size;
 +    int mask_w, mask_h;
 +
 +    uint8_t      *full_mask_data;
 +    FFBoundingBox full_mask_bbox;
 +    uint8_t      *half_mask_data;
 +    FFBoundingBox half_mask_bbox;
 +} RemovelogoContext;
 +
 +/**
 + * Choose a slightly larger mask size to improve performance.
 + *
 + * This function maps the absolute minimum mask size needed to the
 + * mask size we'll actually use. f(x) = x (the smallest that will
 + * work) will produce the sharpest results, but will be quite
 + * jittery. f(x) = 1.25x (what I'm using) is a good tradeoff in my
 + * opinion. This will calculate only at init-time, so you can put a
 + * long expression here without effecting performance.
 + */
 +#define apply_mask_fudge_factor(x) (((x) >> 2) + x)
 +
 +/**
 + * Pre-process an image to give distance information.
 + *
 + * This function takes a bitmap image and converts it in place into a
 + * distance image. A distance image is zero for pixels outside of the
 + * logo and is the Manhattan distance (|dx| + |dy|) from the logo edge
 + * for pixels inside of the logo. This will overestimate the distance,
 + * but that is safe, and is far easier to implement than a proper
 + * pythagorean distance since I'm using a modified erosion algorithm
 + * to compute the distances.
 + *
 + * @param mask image which will be converted from a greyscale image
 + * into a distance image.
 + */
 +static void convert_mask_to_strength_mask(uint8_t *data, int linesize,
 +                                          int w, int h, int min_val,
 +                                          int *max_mask_size)
 +{
 +    int x, y;
 +
 +    /* How many times we've gone through the loop. Used in the
 +       in-place erosion algorithm and to get us max_mask_size later on. */
 +    int current_pass = 0;
 +
 +    /* set all non-zero values to 1 */
 +    for (y = 0; y < h; y++)
 +        for (x = 0; x < w; x++)
 +            data[y*linesize + x] = data[y*linesize + x] > min_val;
 +
 +    /* For each pass, if a pixel is itself the same value as the
 +       current pass, and its four neighbors are too, then it is
 +       incremented. If no pixels are incremented by the end of the
 +       pass, then we go again. Edge pixels are counted as always
 +       excluded (this should be true anyway for any sane mask, but if
 +       it isn't this will ensure that we eventually exit). */
 +    while (1) {
 +        /* If this doesn't get set by the end of this pass, then we're done. */
 +        int has_anything_changed = 0;
 +        uint8_t *current_pixel0 = data, *current_pixel;
 +        current_pass++;
 +
 +        for (y = 1; y < h-1; y++) {
 +            current_pixel = current_pixel0;
 +            for (x = 1; x < w-1; x++) {
 +                /* Apply the in-place erosion transform. It is based
 +                   on the following two premises:
 +                   1 - Any pixel that fails 1 erosion will fail all
 +                       future erosions.
 +
 +                   2 - Only pixels having survived all erosions up to
 +                       the present will be >= to current_pass.
 +                   It doesn't matter if it survived the current pass,
 +                   failed it, or hasn't been tested yet.  By using >=
 +                   instead of ==, we allow the algorithm to work in
 +                   place. */
 +                if ( *current_pixel      >= current_pass &&
 +                    *(current_pixel + 1) >= current_pass &&
 +                    *(current_pixel - 1) >= current_pass &&
 +                    *(current_pixel + w) >= current_pass &&
 +                    *(current_pixel - w) >= current_pass) {
 +                    /* Increment the value since it still has not been
 +                     * eroded, as evidenced by the if statement that
 +                     * just evaluated to true. */
 +                    (*current_pixel)++;
 +                    has_anything_changed = 1;
 +                }
 +                current_pixel++;
 +            }
 +            current_pixel0 += linesize;
 +        }
 +        if (!has_anything_changed)
 +            break;
 +    }
 +
 +    /* Apply the fudge factor, which will increase the size of the
 +     * mask a little to reduce jitter at the cost of more blur. */
 +    for (y = 1; y < h - 1; y++)
 +        for (x = 1; x < w - 1; x++)
 +            data[(y * linesize) + x] = apply_mask_fudge_factor(data[(y * linesize) + x]);
 +
 +    /* As a side-effect, we now know the maximum mask size, which
 +     * we'll use to generate our masks. */
 +    /* Apply the fudge factor to this number too, since we must ensure
 +     * that enough masks are generated. */
 +    *max_mask_size = apply_mask_fudge_factor(current_pass + 1);
 +}
 +
 +static int query_formats(AVFilterContext *ctx)
 +{
 +    enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE };
 +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
 +    return 0;
 +}
 +
 +static int load_mask(uint8_t **mask, int *w, int *h,
 +                     const char *filename, void *log_ctx)
 +{
 +    int ret;
 +    enum PixelFormat pix_fmt;
 +    uint8_t *src_data[4], *gray_data[4];
 +    int src_linesize[4], gray_linesize[4];
 +
 +    /* load image from file */
 +    if ((ret = ff_load_image(src_data, src_linesize, w, h, &pix_fmt, filename, log_ctx)) < 0)
 +        return ret;
 +
 +    /* convert the image to GRAY8 */
 +    if ((ret = ff_scale_image(gray_data, gray_linesize, *w, *h, PIX_FMT_GRAY8,
 +                              src_data, src_linesize, *w, *h, pix_fmt,
 +                              log_ctx)) < 0)
 +        goto end;
 +
 +    /* copy mask to a newly allocated array */
 +    *mask = av_malloc(*w * *h);
 +    if (!*mask)
 +        ret = AVERROR(ENOMEM);
 +    av_image_copy_plane(*mask, *w, gray_data[0], gray_linesize[0], *w, *h);
 +
 +end:
 +    av_free(src_data[0]);
 +    av_free(gray_data[0]);
 +    return ret;
 +}
 +
 +/**
 + * Generate a scaled down image with half width, height, and intensity.
 + *
 + * This function not only scales down an image, but halves the value
 + * in each pixel too. The purpose of this is to produce a chroma
 + * filter image out of a luma filter image. The pixel values store the
 + * distance to the edge of the logo and halving the dimensions halves
 + * the distance. This function rounds up, because a downwards rounding
 + * error could cause the filter to fail, but an upwards rounding error
 + * will only cause a minor amount of excess blur in the chroma planes.
 + */
 +static void generate_half_size_image(const uint8_t *src_data, int src_linesize,
 +                                     uint8_t *dst_data, int dst_linesize,
 +                                     int src_w, int src_h,
 +                                     int *max_mask_size)
 +{
 +    int x, y;
 +
 +    /* Copy over the image data, using the average of 4 pixels for to
 +     * calculate each downsampled pixel. */
 +    for (y = 0; y < src_h/2; y++) {
 +        for (x = 0; x < src_w/2; x++) {
 +            /* Set the pixel if there exists a non-zero value in the
 +             * source pixels, else clear it. */
 +            dst_data[(y * dst_linesize) + x] =
 +                src_data[((y << 1) * src_linesize) + (x << 1)] ||
 +                src_data[((y << 1) * src_linesize) + (x << 1) + 1] ||
 +                src_data[(((y << 1) + 1) * src_linesize) + (x << 1)] ||
 +                src_data[(((y << 1) + 1) * src_linesize) + (x << 1) + 1];
 +            dst_data[(y * dst_linesize) + x] = FFMIN(1, dst_data[(y * dst_linesize) + x]);
 +        }
 +    }
 +
 +    convert_mask_to_strength_mask(dst_data, dst_linesize,
 +                                  src_w/2, src_h/2, 0, max_mask_size);
 +}
 +
++static av_cold int init(AVFilterContext *ctx, const char *args)
 +{
 +    RemovelogoContext *removelogo = ctx->priv;
 +    int ***mask;
 +    int ret = 0;
 +    int a, b, c, w, h;
 +    int full_max_mask_size, half_max_mask_size;
 +
 +    if (!args) {
 +        av_log(ctx, AV_LOG_ERROR, "An image file must be specified as argument\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    /* Load our mask image. */
 +    if ((ret = load_mask(&removelogo->full_mask_data, &w, &h, args, ctx)) < 0)
 +        return ret;
 +    removelogo->mask_w = w;
 +    removelogo->mask_h = h;
 +
 +    convert_mask_to_strength_mask(removelogo->full_mask_data, w, w, h,
 +                                  16, &full_max_mask_size);
 +
 +    /* Create the scaled down mask image for the chroma planes. */
 +    if (!(removelogo->half_mask_data = av_mallocz(w/2 * h/2)))
 +        return AVERROR(ENOMEM);
 +    generate_half_size_image(removelogo->full_mask_data, w,
 +                             removelogo->half_mask_data, w/2,
 +                             w, h, &half_max_mask_size);
 +
 +    removelogo->max_mask_size = FFMAX(full_max_mask_size, half_max_mask_size);
 +
 +    /* Create a circular mask for each size up to max_mask_size. When
 +       the filter is applied, the mask size is determined on a pixel
 +       by pixel basis, with pixels nearer the edge of the logo getting
 +       smaller mask sizes. */
 +    mask = (int ***)av_malloc(sizeof(int **) * (removelogo->max_mask_size + 1));
 +    if (!mask)
 +        return AVERROR(ENOMEM);
 +
 +    for (a = 0; a <= removelogo->max_mask_size; a++) {
 +        mask[a] = (int **)av_malloc(sizeof(int *) * ((a * 2) + 1));
 +        if (!mask[a])
 +            return AVERROR(ENOMEM);
 +        for (b = -a; b <= a; b++) {
 +            mask[a][b + a] = (int *)av_malloc(sizeof(int) * ((a * 2) + 1));
 +            if (!mask[a][b + a])
 +                return AVERROR(ENOMEM);
 +            for (c = -a; c <= a; c++) {
 +                if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */
 +                    mask[a][b + a][c + a] = 1;
 +                else
 +                    mask[a][b + a][c + a] = 0;
 +            }
 +        }
 +    }
 +    removelogo->mask = mask;
 +
 +    /* Calculate our bounding rectangles, which determine in what
 +     * region the logo resides for faster processing. */
 +    ff_calculate_bounding_box(&removelogo->full_mask_bbox, removelogo->full_mask_data, w, w, h, 0);
 +    ff_calculate_bounding_box(&removelogo->half_mask_bbox, removelogo->half_mask_data, w/2, w/2, h/2, 0);
 +
 +#define SHOW_LOGO_INFO(mask_type)                                       \
 +    av_log(ctx, AV_LOG_INFO, #mask_type " x1:%d x2:%d y1:%d y2:%d max_mask_size:%d\n", \
 +           removelogo->mask_type##_mask_bbox.x1, removelogo->mask_type##_mask_bbox.x2, \
 +           removelogo->mask_type##_mask_bbox.y1, removelogo->mask_type##_mask_bbox.y2, \
 +           mask_type##_max_mask_size);
 +    SHOW_LOGO_INFO(full);
 +    SHOW_LOGO_INFO(half);
 +
 +    return 0;
 +}
 +
 +static int config_props_input(AVFilterLink *inlink)
 +{
 +    AVFilterContext *ctx = inlink->dst;
 +    RemovelogoContext *removelogo = ctx->priv;
 +
 +    if (inlink->w != removelogo->mask_w || inlink->h != removelogo->mask_h) {
 +        av_log(ctx, AV_LOG_INFO,
 +               "Mask image size %dx%d does not match with the input video size %dx%d\n",
 +               removelogo->mask_w, removelogo->mask_h, inlink->w, inlink->h);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    return 0;
 +}
 +
 +/**
 + * Blur image.
 + *
 + * It takes a pixel that is inside the mask and blurs it. It does so
 + * by finding the average of all the pixels within the mask and
 + * outside of the mask.
 + *
 + * @param mask_data  the mask plane to use for averaging
 + * @param image_data the image plane to blur
 + * @param w width of the image
 + * @param h height of the image
 + * @param x x-coordinate of the pixel to blur
 + * @param y y-coordinate of the pixel to blur
 + */
 +static unsigned int blur_pixel(int ***mask,
 +                               const uint8_t *mask_data, int mask_linesize,
 +                               uint8_t       *image_data, int image_linesize,
 +                               int w, int h, int x, int y)
 +{
 +    /* Mask size tells how large a circle to use. The radius is about
 +     * (slightly larger than) mask size. */
 +    int mask_size;
 +    int start_posx, start_posy, end_posx, end_posy;
 +    int i, j;
 +    unsigned int accumulator = 0, divisor = 0;
 +    /* What pixel we are reading out of the circular blur mask. */
 +    const uint8_t *image_read_position;
 +    /* What pixel we are reading out of the filter image. */
 +    const uint8_t *mask_read_position;
 +
 +    /* Prepare our bounding rectangle and clip it if need be. */
 +    mask_size  = mask_data[y * mask_linesize + x];
 +    start_posx = FFMAX(0, x - mask_size);
 +    start_posy = FFMAX(0, y - mask_size);
 +    end_posx   = FFMIN(w - 1, x + mask_size);
 +    end_posy   = FFMIN(h - 1, y + mask_size);
 +
 +    image_read_position = image_data + image_linesize * start_posy + start_posx;
 +    mask_read_position  = mask_data  + mask_linesize  * start_posy + start_posx;
 +
 +    for (j = start_posy; j <= end_posy; j++) {
 +        for (i = start_posx; i <= end_posx; i++) {
 +            /* Check if this pixel is in the mask or not. Only use the
 +             * pixel if it is not. */
 +            if (!(*mask_read_position) && mask[mask_size][i - start_posx][j - start_posy]) {
 +                accumulator += *image_read_position;
 +                divisor++;
 +            }
 +
 +            image_read_position++;
 +            mask_read_position++;
 +        }
 +
 +        image_read_position += (image_linesize - ((end_posx + 1) - start_posx));
 +        mask_read_position  += (mask_linesize - ((end_posx + 1) - start_posx));
 +    }
 +
 +    /* If divisor is 0, it means that not a single pixel is outside of
 +       the logo, so we have no data.  Else we need to normalise the
 +       data using the divisor. */
 +    return divisor == 0 ? 255:
 +        (accumulator + (divisor / 2)) / divisor;  /* divide, taking into account average rounding error */
 +}
 +
 +/**
 + * Blur image plane using a mask.
 + *
 + * @param source The image to have it's logo removed.
 + * @param destination Where the output image will be stored.
 + * @param source_stride How far apart (in memory) two consecutive lines are.
 + * @param destination Same as source_stride, but for the destination image.
 + * @param width Width of the image. This is the same for source and destination.
 + * @param height Height of the image. This is the same for source and destination.
 + * @param is_image_direct If the image is direct, then source and destination are
 + *        the same and we can save a lot of time by not copying pixels that
 + *        haven't changed.
 + * @param filter The image that stores the distance to the edge of the logo for
 + *        each pixel.
 + * @param logo_start_x smallest x-coordinate that contains at least 1 logo pixel.
 + * @param logo_start_y smallest y-coordinate that contains at least 1 logo pixel.
 + * @param logo_end_x   largest x-coordinate that contains at least 1 logo pixel.
 + * @param logo_end_y   largest y-coordinate that contains at least 1 logo pixel.
 + *
 + * This function processes an entire plane. Pixels outside of the logo are copied
 + * to the output without change, and pixels inside the logo have the de-blurring
 + * function applied.
 + */
 +static void blur_image(int ***mask,
 +                       const uint8_t *src_data,  int src_linesize,
 +                             uint8_t *dst_data,  int dst_linesize,
 +                       const uint8_t *mask_data, int mask_linesize,
 +                       int w, int h, int direct,
 +                       FFBoundingBox *bbox)
 +{
 +    int x, y;
 +    uint8_t *dst_line;
 +    const uint8_t *src_line;
 +
 +    if (!direct)
 +        av_image_copy_plane(dst_data, dst_linesize, src_data, src_linesize, w, h);
 +
 +    for (y = bbox->y1; y <= bbox->y2; y++) {
 +        src_line = src_data + src_linesize * y;
 +        dst_line = dst_data + dst_linesize * y;
 +
 +        for (x = bbox->x1; x <= bbox->x2; x++) {
 +             if (mask_data[y * mask_linesize + x]) {
 +                /* Only process if we are in the mask. */
 +                 dst_line[x] = blur_pixel(mask,
 +                                          mask_data, mask_linesize,
 +                                          dst_data, dst_linesize,
 +                                          w, h, x, y);
 +            } else {
 +                /* Else just copy the data. */
 +                if (!direct)
 +                    dst_line[x] = src_line[x];
 +            }
 +        }
 +    }
 +}
 +
 +static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
 +{
 +    AVFilterLink *outlink = inlink->dst->outputs[0];
 +    AVFilterBufferRef *outpicref;
 +
 +    if (inpicref->perms & AV_PERM_PRESERVE) {
 +        outpicref = ff_get_video_buffer(outlink, AV_PERM_WRITE,
 +                                              outlink->w, outlink->h);
 +        avfilter_copy_buffer_ref_props(outpicref, inpicref);
 +        outpicref->video->w = outlink->w;
 +        outpicref->video->h = outlink->h;
 +    } else
 +        outpicref = inpicref;
 +
 +    outlink->out_buf = outpicref;
 +    ff_start_frame(outlink, avfilter_ref_buffer(outpicref, ~0));
 +}
 +
 +static void end_frame(AVFilterLink *inlink)
 +{
 +    RemovelogoContext *removelogo = inlink->dst->priv;
 +    AVFilterLink *outlink = inlink->dst->outputs[0];
 +    AVFilterBufferRef *inpicref  = inlink ->cur_buf;
 +    AVFilterBufferRef *outpicref = outlink->out_buf;
 +    int direct = inpicref == outpicref;
 +
 +    blur_image(removelogo->mask,
 +               inpicref ->data[0], inpicref ->linesize[0],
 +               outpicref->data[0], outpicref->linesize[0],
 +               removelogo->full_mask_data, inlink->w,
 +               inlink->w, inlink->h, direct, &removelogo->full_mask_bbox);
 +    blur_image(removelogo->mask,
 +  &nb