[FFmpeg-devel] [PATCH] Add af_resample - sample fmt and channel layout conversion filter
Stefano Sabatini
stefano.sabatini-lala
Mon Aug 23 17:13:29 CEST 2010
On date Monday 2010-08-23 00:11:52 -0700, S.N. Hemanth Meenakshisundaram encoded:
> Fixed the query formats function so that a resample filter can be
> inserted between filters with incompatible sample formats.
>
> ---
> libavfilter/Makefile | 1 +
> libavfilter/af_resample.c | 519 +++++++++++++++++++++++++++++++++++++++++++++
> libavfilter/allfilters.c | 1 +
> 3 files changed, 521 insertions(+), 0 deletions(-)
> create mode 100644 libavfilter/af_resample.c
>
>
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 443a5c6..0a80c3d 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -15,6 +15,7 @@ OBJS = allfilters.o \
> parseutils.o \
>
> OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o
> +OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o
>
> OBJS-$(CONFIG_ASPECT_FILTER) += vf_aspect.o
> OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o
> diff --git a/libavfilter/af_resample.c b/libavfilter/af_resample.c
> new file mode 100644
> index 0000000..329b334
> --- /dev/null
> +++ b/libavfilter/af_resample.c
> @@ -0,0 +1,519 @@
> +/*
> + * copyright (c) 2010 S.N. Hemanth Meenakshisundaram <smeenaks at ucsd.edu>
> + * based on code in libavcodec/resample.c by Fabrice Bellard
> + * and libavcodec/audioconvert.c by Michael Neidermayer
> + *
> + * 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
> + * resample audio filter
> + */
> +
> +#include "avfilter.h"
> +#include "libavcodec/audioconvert.h"
> +
> +typedef struct {
> +
> + short reconfig_channel_layout; ///< set when channel layout of incoming buffer changes
> + short reconfig_sample_fmt; ///< set when sample format of incoming buffer changes
> +
> + enum SampleFormat in_sample_fmt; ///< default incoming sample format expected
> + enum SampleFormat out_sample_fmt; ///< output sample format
> + int64_t in_channel_layout; ///< default incoming channel layout expected
> + int64_t out_channel_layout; ///< output channel layout
> +
> + int in_samples_nb; ///< stores number of samples in previous incoming buffer
> + AVFilterBufferRef *s16_samples; ///< stores temporary audio data in s16 sample format for channel layout conversions
> + AVFilterBufferRef *s16_samples_ptr; ///< duplicate pointer to audio data in s16 sample format
> + AVFilterBufferRef *temp_samples; ///< stores temporary audio data in s16 sample format after channel layout conversions
> + AVFilterBufferRef *temp_samples_ptr; ///< duplicate pointer to audio data after channel layout conversions
> + AVFilterBufferRef *out_samples; ///< stores audio data after required sample format and channel layout conversions
> + AVFilterBufferRef *out_samples_ptr; ///< duplicate pointer to audio data after required conversions
> +
> + void (*channel_conversion) (uint8_t *out[], uint8_t *in[], int , int); ///< channel conversion routine that will
> + ///< point to one of the routines below
> +} ResampleContext;
> +
> +/**
> + * All of the routines below are for packed audio data. SDL accepts packed data
> + * only and current ffplay also assumes packed data only at all times.
> + */
> +
> +/* Optimized stereo to mono and mono to stereo routines - common case */
> +static void stereo_to_mono(uint8_t *out[], uint8_t *in[], int samples_nb, int in_channels)
> +{
> + short *input = (short *) in[0];
> + short *output = (short *) out[0];
> +
> + while (samples_nb >= 4) {
> + output[0] = (input[0] + input[1]) >> 1;
> + output[1] = (input[2] + input[3]) >> 1;
> + output[2] = (input[4] + input[5]) >> 1;
> + output[3] = (input[6] + input[7]) >> 1;
> + output += 4;
> + input += 8;
> + samples_nb -= 4;
> + }
> + while (samples_nb > 0) {
> + output[0] = (input[0] + input[1]) >> 1;
> + output++;
> + input += 2;
> + samples_nb--;
> + }
> +}
> +
> +static void mono_to_stereo(uint8_t *out[], uint8_t *in[], int samples_nb, int in_channels)
> +{
> + int v;
> + short *input = (short *) in[0];
> + short *output = (short *) out[0];
> +
> +
> + while (samples_nb >= 4) {
> + v = input[0]; output[0] = v; output[1] = v;
> + v = input[1]; output[2] = v; output[3] = v;
> + v = input[2]; output[4] = v; output[5] = v;
> + v = input[3]; output[6] = v; output[7] = v;
> + output += 8;
> + input += 4;
> + samples_nb -= 4;
> + }
> + while (samples_nb > 0) {
> + v = input[0]; output[0] = v; output[1] = v;
> + output += 2;
> + input += 1;
> + samples_nb--;
> + }
> +}
> +
> +/**
> + * This is for when we have more than 2 input channels, need to downmix to
> + * stereo and do not have a conversion formula available. We just use first
> + * two input channels - left and right. This is a placeholder until more
> + * conversion functions are written.
> + */
> +static void stereo_downmix(uint8_t *out[], uint8_t *in[], int samples_nb, int in_channels)
> +{
> + int i;
> + short *output = (short *) out[0];
> + short *input = (short *) out[0];
> +
> + for (i = 0; i < samples_nb; i++) {
> + *output++ = *input++;
> + *output++ = *input++;
> + input+=(in_channels-2);
> + }
> +}
> +
> +/**
> + * This is for when we have more than 2 input channels, need to downmix to mono
> + * and do not have a conversion formula available. We just use first two input
> + * channels - left and right. This is a placeholder until more conversion
> + * functions are written.
> + */
> +static void mono_downmix(uint8_t *out[], uint8_t *in[], int samples_nb, int in_channels)
> +{
> + int i;
> + short *input = (short *) in[0];
> + short *output = (short *) out[0];
> + short left, right;
> +
> + for (i = 0; i < samples_nb; i++) {
> + left = *input++;
> + right = *input++;
> + *output++ = (left>>1)+(right>>1);
> + input+=(in_channels-2);
> + }
> +}
> +
> +/* Stereo to 5.1 output */
> +static void ac3_5p1_mux(uint8_t *out[], uint8_t *in[], int samples_nb, int in_channels)
> +{
> + int i;
> + short *output = (short *) out[0];
> + short *input = (short *) in[0];
> + short left, right;
> +
> + for (i = 0; i < samples_nb; i++) {
> + left = *input++; /* Grab next left sample */
> + right = *input++; /* Grab next right sample */
> + *output++ = left; /* left */
> + *output++ = right; /* right */
> + *output++ = (left>>1)+(right>>1); /* center */
> + *output++ = 0; /* low freq */
> + *output++ = 0; /* FIXME: left surround is either -3dB, -6dB or -9dB of stereo left */
> + *output++ = 0; /* FIXME: right surroud is either -3dB, -6dB or -9dB of stereo right */
> + }
> +}
I suppose this code is taken from libavcodec, so there is no point
into reviewing it. Maybe a better idea, rather than duplicate the
code, could be to try to export this API as internal functions
(ff_prefixed).
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> + ResampleContext *resample = ctx->priv;
> + resample->out_sample_fmt = -1;
> + resample->out_channel_layout = -1;
> +
> + // FIXME: Add code to parse string arguments as well.
> + if (args){
> + sscanf(args, "%d:%ld", &resample->out_sample_fmt,
> + &resample->out_channel_layout);
possibly it should take "%s:%s", sample_fmt <=> int mapping should be
already supported by the API, as for the channel layout there is your
patch waiting for review.
> + }
> +
> + /**
> + * sanity check params
> + * SAMPLE_FMT_NONE is a valid value for out_sample_fmt and indicates no
> + * change in sample format.
> + * -1 is a valid value for out_channel_layout and indicates no change
> + * in channel layout.
> + */
> +
> + if (resample->out_sample_fmt >= SAMPLE_FMT_NB ||
> + resample->out_sample_fmt < SAMPLE_FMT_NONE) {
> + av_log(ctx, AV_LOG_ERROR,
> + "Invalid sample format %d, cannot resample.\n",
> + resample->out_sample_fmt);
Possibly print a string here for the sample format.
> + return AVERROR(EINVAL);
> + }
> + if ((resample->out_channel_layout > CH_LAYOUT_STEREO_DOWNMIX ||
> + resample->out_channel_layout < CH_LAYOUT_STEREO) &&
> + (resample->out_channel_layout != -1)) {
> + av_log(ctx, AV_LOG_ERROR,
> + "Invalid channel layout %ld, cannot resample.\n",
> + resample->out_channel_layout);
Same here, better to give a string representation.
> + return AVERROR(EINVAL);
> + }
> +
> + /* Set default values for expected incoming sample format and channel layout */
> + resample->in_channel_layout = CH_LAYOUT_STEREO;
> + resample->in_sample_fmt = SAMPLE_FMT_S16;
> + resample->in_samples_nb = 0;
> + /* We do not yet know the channel conversion function to be used */
> + resample->channel_conversion = NULL;
> +
> + return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> + ResampleContext *resample = ctx->priv;
> + if (resample->s16_samples)
> + avfilter_unref_buffer(resample->s16_samples);
> + if (resample->temp_samples)
> + avfilter_unref_buffer(resample->temp_samples);
> + if (resample->out_samples)
> + avfilter_unref_buffer(resample->out_samples);
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + AVFilterFormats *formats;
> + enum SampleFormat sample_fmt;
> + int ret;
> +
> + if (ctx->inputs[0]) {
> + formats = NULL;
> + for (sample_fmt = 0; sample_fmt < SAMPLE_FMT_NB; sample_fmt++)
> + if ((ret = avfilter_add_format(&formats, sample_fmt)) < 0) {
> + avfilter_formats_unref(&formats);
> + return ret;
> + }
formats = avfilter_all_formats()
> + avfilter_formats_ref(formats, &ctx->inputs[0]->out_formats);
> + }
> + if (ctx->outputs[0]) {
> + formats = NULL;
> + for (sample_fmt = 0; sample_fmt < SAMPLE_FMT_NB; sample_fmt++)
> + if ((ret = avfilter_add_format(&formats, sample_fmt)) < 0) {
> + avfilter_formats_unref(&formats);
> + return ret;
Same
> + }
> + avfilter_formats_ref(formats, &ctx->outputs[0]->in_formats);
> + }
> +
> + return 0;
> +}
> +
> +static void convert_channel_layout(AVFilterLink *link)
> +{
> + ResampleContext *resample = link->dst->priv;
> + AVFilterBufferRef *insamples = resample->s16_samples_ptr;
> + AVFilterBufferRef *outsamples = resample->temp_samples;
> + unsigned int num_ip_channels = avcodec_channel_layout_num_channels(resample->in_channel_layout);
> +
> + if (insamples)
> + resample->in_channel_layout = insamples->audio->channel_layout;
> +
> + /* Init stage or input channels changed, so reconfigure conversion function pointer */
> + if (resample->reconfig_channel_layout || !resample->channel_conversion) {
> +
> + int64_t in_channel = resample->in_channel_layout;
> + int64_t out_channel = resample->out_channel_layout;
> +
> + int num_channels = avcodec_channel_layout_num_channels(resample->out_channel_layout);
> + int out_sample_size = av_get_bits_per_sample_format(insamples->format) >> 3;
> +
> + int size = num_channels*out_sample_size*insamples->audio->samples_nb;
> +
> + if (outsamples)
> + avfilter_unref_buffer(outsamples);
> + outsamples = avfilter_get_audio_buffer(link, AV_PERM_WRITE,
> + insamples->format, size,
> + out_channel, 0);
> + /*
> + * Pick appropriate channel conversion function based on input-output channel layouts.
> + * If no suitable conversion function is available, downmix to stereo and set buffer
> + * channel layout to stereo.
> + *
> + * FIXME: Add error handling if channel conversion is unsupported, more channel conversion
> + * routines and finally the ability to handle various stride lengths (sample formats).
> + */
> +
> + if ((in_channel == CH_LAYOUT_STEREO) &&
> + (out_channel == CH_LAYOUT_MONO))
> + resample->channel_conversion = stereo_to_mono;
> + else if ((in_channel == CH_LAYOUT_MONO) &&
> + (out_channel == CH_LAYOUT_STEREO))
> + resample->channel_conversion = mono_to_stereo;
> + else if ((in_channel == CH_LAYOUT_STEREO) &&
> + (out_channel == CH_LAYOUT_5POINT1))
> + resample->channel_conversion = ac3_5p1_mux;
> + else if (out_channel == CH_LAYOUT_MONO)
> + resample->channel_conversion = mono_downmix;
> + else {
> + resample->channel_conversion = stereo_downmix;
> + outsamples->audio->channel_layout = CH_LAYOUT_STEREO;
> + }
> +
> + }
> +
> + if (outsamples && insamples) {
> + resample->channel_conversion(outsamples->data, insamples->data,
> + outsamples->audio->samples_nb,
> + num_ip_channels);
> + }
> + resample->temp_samples = outsamples;
> + resample->temp_samples_ptr = outsamples;
> +}
> +
> +static void convert_sample_fmt(AVFilterLink *link, AVFilterBufferRef *insamples,
> + AVFilterBufferRef *outsamples, int len,
> + int out_sample_size, int out_channels, int fmt_pair,
> + int planar)
> +{
> + int in_sample_size = av_get_bits_per_sample_format(insamples->format) >> 3;
> + int ch = 0;
> +
> + do {
> + const uint8_t *pi = insamples->data[ch];
> + uint8_t *po = outsamples->data[ch];
> + int instride = (planar) ? insamples->linesize[ch] : in_sample_size;
> + int outstride = (planar) ? outsamples->linesize[ch] : out_sample_size;
> + uint8_t *end = po + outstride*len;
> +
> + if(!outsamples->data[ch])
> + continue;
> +
> +#define CONV(ofmt, otype, ifmt, expr)\
> +if (fmt_pair == ofmt + SAMPLE_FMT_NB*ifmt) {\
> + do {\
> + *(otype*)po = expr; pi += instride; po += outstride;\
> + } while(po < end);\
> +}
> +
> + CONV(SAMPLE_FMT_U8 , uint8_t, SAMPLE_FMT_U8 , *(const uint8_t*)pi)
> + else CONV(SAMPLE_FMT_S16, int16_t, SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<8)
> + else CONV(SAMPLE_FMT_S32, int32_t, SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<24)
> + else CONV(SAMPLE_FMT_FLT, float , SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7)))
> + else CONV(SAMPLE_FMT_DBL, double , SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7)))
> + else CONV(SAMPLE_FMT_U8 , uint8_t, SAMPLE_FMT_S16, (*(const int16_t*)pi>>8) + 0x80)
> + else CONV(SAMPLE_FMT_S16, int16_t, SAMPLE_FMT_S16, *(const int16_t*)pi)
> + else CONV(SAMPLE_FMT_S32, int32_t, SAMPLE_FMT_S16, *(const int16_t*)pi<<16)
> + else CONV(SAMPLE_FMT_FLT, float , SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15)))
> + else CONV(SAMPLE_FMT_DBL, double , SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15)))
> + else CONV(SAMPLE_FMT_U8 , uint8_t, SAMPLE_FMT_S32, (*(const int32_t*)pi>>24) + 0x80)
> + else CONV(SAMPLE_FMT_S16, int16_t, SAMPLE_FMT_S32, *(const int32_t*)pi>>16)
> + else CONV(SAMPLE_FMT_S32, int32_t, SAMPLE_FMT_S32, *(const int32_t*)pi)
> + else CONV(SAMPLE_FMT_FLT, float , SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1<<31)))
> + else CONV(SAMPLE_FMT_DBL, double , SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1<<31)))
> + else CONV(SAMPLE_FMT_U8 , uint8_t, SAMPLE_FMT_FLT, av_clip_uint8( lrintf(*(const float*)pi * (1<<7)) + 0x80))
> + else CONV(SAMPLE_FMT_S16, int16_t, SAMPLE_FMT_FLT, av_clip_int16( lrintf(*(const float*)pi * (1<<15))))
> + else CONV(SAMPLE_FMT_S32, int32_t, SAMPLE_FMT_FLT, av_clipl_int32(llrintf(*(const float*)pi * (1U<<31))))
> + else CONV(SAMPLE_FMT_FLT, float , SAMPLE_FMT_FLT, *(const float*)pi)
> + else CONV(SAMPLE_FMT_DBL, double , SAMPLE_FMT_FLT, *(const float*)pi)
> + else CONV(SAMPLE_FMT_U8 , uint8_t, SAMPLE_FMT_DBL, av_clip_uint8( lrint(*(const double*)pi * (1<<7)) + 0x80))
> + else CONV(SAMPLE_FMT_S16, int16_t, SAMPLE_FMT_DBL, av_clip_int16( lrint(*(const double*)pi * (1<<15))))
> + else CONV(SAMPLE_FMT_S32, int32_t, SAMPLE_FMT_DBL, av_clipl_int32(llrint(*(const double*)pi * (1U<<31))))
> + else CONV(SAMPLE_FMT_FLT, float , SAMPLE_FMT_DBL, *(const double*)pi)
> + else CONV(SAMPLE_FMT_DBL, double , SAMPLE_FMT_DBL, *(const double*)pi)
> +
> + } while (ch < insamples->audio->planar*out_channels);
why not to call av_audio_convert()?
> +
> + return;
> +}
Again, I'd like this
> +static void convert_sample_format_wrapper(AVFilterLink *link)
> +{
> + ResampleContext *resample = link->dst->priv;
> + AVFilterBufferRef *insamples = resample->temp_samples_ptr;
> + AVFilterBufferRef *outsamples = resample->out_samples;
> + int out_channels, out_sample_size, planar, len, fmt_pair;
> +
> + /* Here, out_channels is same as input channels, we are only changing
> + * sample format. */
> + /* FIXME: Need to use hamming weight counting function instead once it is
> + * added to libavutil. */
> + out_channels = avcodec_channel_layout_num_channels(insamples->audio->channel_layout);
> + out_sample_size = av_get_bits_per_sample_format(resample->out_sample_fmt) >> 3;
> +
> + planar = insamples->audio->planar;
> + len = (planar) ? insamples->audio->samples_nb :
> + insamples->audio->samples_nb*out_channels;
> + fmt_pair = insamples->format*SAMPLE_FMT_NB+resample->out_sample_fmt;
> +
> + if (resample->reconfig_sample_fmt || !outsamples ||
> + !outsamples->audio->size) {
> +
> + int size = out_channels*out_sample_size*insamples->audio->samples_nb;
> +
> + if (outsamples)
> + avfilter_unref_buffer(outsamples);
> + outsamples = avfilter_get_audio_buffer(link, AV_PERM_WRITE,
> + resample->out_sample_fmt, size,
> + insamples->audio->channel_layout, 0);
> +
> + }
> +
> + /* Timestamp and sample rate can change even while sample format/channel layout remain the same */
> + outsamples->pts = insamples->pts;
> + outsamples->audio->sample_rate = insamples->audio->sample_rate;
> +
> + convert_sample_fmt(link, insamples, outsamples, len, out_sample_size,
> + out_channels, fmt_pair, planar);
> + resample->out_samples = outsamples;
> + resample->out_samples_ptr = outsamples;
> +
> +}
> +
> +static void convert_s16_format_wrapper(AVFilterLink *link, AVFilterBufferRef *insamples)
> +{
> + ResampleContext *resample = link->dst->priv;
> + AVFilterBufferRef *outsamples = resample->s16_samples;
> + int out_channels, planar, len, fmt_pair;
> +
> + /* Here, out_channels is same as input channels, we are only changing sample format */
> + out_channels = avcodec_channel_layout_num_channels(insamples->audio->channel_layout);
> +
> + planar = insamples->audio->planar;
> + len = (planar) ? insamples->audio->samples_nb : insamples->audio->samples_nb*out_channels;
> + fmt_pair = insamples->format*SAMPLE_FMT_NB+SAMPLE_FMT_S16;
> +
> + if (resample->reconfig_sample_fmt || !outsamples || !outsamples->audio->size) {
> +
> + int size = out_channels*2*insamples->audio->samples_nb;
> +
> + if (outsamples)
> + avfilter_unref_buffer(outsamples);
> + outsamples = avfilter_get_audio_buffer(link, AV_PERM_WRITE,
> + SAMPLE_FMT_S16, size,
> + insamples->audio->channel_layout, 0);
> +
> + }
> +
> + /* Timestamp and sample rate can change even while sample format/channel layout remain the same */
> + outsamples->pts = insamples->pts;
> + outsamples->audio->sample_rate = insamples->audio->sample_rate;
> +
> + convert_sample_fmt(link, insamples, outsamples, len, 2, out_channels,
> + fmt_pair, planar);
> + resample->s16_samples = outsamples;
> + resample->s16_samples_ptr = outsamples;
> +}
> +
> +static int config_props(AVFilterLink *link)
> +{
> + AVFilterContext *ctx = link->dst;
> + ResampleContext *resample = ctx->priv;
> +
> + if (resample->out_channel_layout == -1)
> + resample->out_channel_layout = link->channel_layout;
> +
> + if (resample->out_sample_fmt == -1)
> + resample->out_sample_fmt = link->format;
> +
> + return 0;
> +}
> +
> +static void filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
> +{
> + ResampleContext *resample = link->dst->priv;
> + AVFilterLink *outlink = link->dst->outputs[0];
> + short samples_nb_changed = 0;
> +
> + /**
> + * If input data of this buffer differs from the earlier buffer/s, set flag
> + * to reconfigure the channel and sample format conversions.
> + */
> +
> + samples_nb_changed = (samplesref->audio->samples_nb != resample->in_samples_nb);
> + resample->in_samples_nb = samplesref->audio->samples_nb;
> + resample->reconfig_sample_fmt = (samplesref->format != resample->in_sample_fmt) || samples_nb_changed;
> + resample->in_sample_fmt = samplesref->format;
> + resample->reconfig_channel_layout = (samplesref->audio->channel_layout != resample->in_channel_layout) || samples_nb_changed;
> + resample->in_channel_layout = samplesref->audio->channel_layout;
> +
> + /* Convert to s16 sample format first, then to desired channel layout and finally to desired sample format */
> +
> + if (samplesref->format == SAMPLE_FMT_S16)
> + resample->s16_samples_ptr = samplesref;
> + else
> + convert_s16_format_wrapper(link, samplesref);
> +
> + if (samplesref->audio->channel_layout == resample->out_channel_layout)
> + resample->temp_samples_ptr = resample->s16_samples_ptr;
> + else
> + convert_channel_layout(link);
> +
> + if (resample->out_sample_fmt == SAMPLE_FMT_S16)
> + resample->out_samples_ptr = resample->temp_samples_ptr;
> + else
> + convert_sample_format_wrapper(link);
> +
> + avfilter_filter_samples(outlink, avfilter_ref_buffer(resample->out_samples_ptr, ~0));
> + avfilter_unref_buffer(samplesref);
> +}
> +
> +AVFilter avfilter_af_resample = {
> + .name = "resample",
> + .description = NULL_IF_CONFIG_SMALL("Reformat the input audio to sample_fmt:channel_layout."),
> +
> + .init = init,
> + .uninit = uninit,
> +
> + .query_formats = query_formats,
> +
> + .priv_size = sizeof(ResampleContext),
> +
> + .inputs = (AVFilterPad[]) {{ .name = "default",
> + .type = AVMEDIA_TYPE_AUDIO,
> + .filter_samples = filter_samples,
> + .config_props = config_props,
> + .min_perms = AV_PERM_READ, },
> + { .name = NULL}},
> + .outputs = (AVFilterPad[]) {{ .name = "default",
> + .type = AVMEDIA_TYPE_AUDIO, },
> + { .name = NULL}},
> +};
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 66bcf96..ee64042 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -35,6 +35,7 @@ void avfilter_register_all(void)
> initialized = 1;
>
> REGISTER_FILTER (ANULL, anull, af);
> + REGISTER_FILTER (RESAMPLE, resample, af);
Missing docs (not high priority, feel free to add it when we're
approaching to the final version).
Regards.
--
FFmpeg = Fiendish Funny Martial Purposeless Ecletic Gorilla
More information about the ffmpeg-devel
mailing list