[FFmpeg-devel] [PATCH 2/4] pan: add channel mapping capability.
Clément Bœsch
ubitux at gmail.com
Wed Jan 18 16:37:16 CET 2012
On Wed, Jan 18, 2012 at 03:05:09PM +0100, Nicolas George wrote:
> Le nonidi 29 nivôse, an CCXX, Clément Bœsch a écrit :
> > From: Clément Bœsch <clement.boesch at smartjog.com>
> >
> > ---
> > doc/filters.texi | 43 +++++++++++++++++++++
> > libavfilter/af_pan.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++-
> > 2 files changed, 142 insertions(+), 1 deletions(-)
> >
> > diff --git a/doc/filters.texi b/doc/filters.texi
> > index 3c9f554..7d24389 100644
> > --- a/doc/filters.texi
> > +++ b/doc/filters.texi
> > @@ -315,6 +315,9 @@ Ported from SoX.
> > Mix channels with specific gain levels. The filter accepts the output
> > channel layout followed by a set of channels definitions.
> >
> > +This filter is also designed to remap efficiently the channels of an audio
> > +stream.
> > +
> > The filter accepts parameters of the form:
> > "@var{l}:@var{outdef}:@var{outdef}:..."
> >
> > @@ -342,6 +345,8 @@ If the `=' in a channel specification is replaced by `<', then the gains for
> > that specification will be renormalized so that the total is 1, thus
> > avoiding clipping noise.
> >
> > + at subsection Mixing examples
> > +
> > For example, if you want to down-mix from stereo to mono, but with a bigger
> > factor for the left channel:
> > @example
> > @@ -358,6 +363,44 @@ Note that @command{ffmpeg} integrates a default down-mix (and up-mix) system
> > that should be preferred (see "-ac" option) unless you have very specific
> > needs.
> >
> > + at subsection Remapping examples
> > +
> > +The channel remapping will be effective if, and only if:
> > +
> > + at itemize
> > + at item gain coefficients are zeroes or ones,
> > + at item only one input per channel output,
> > + at item the number of output channels is supported by libswresample.
>
> Maybe say the value of the maximum number of channels?
>
Added (plus a SWR_CH_MAX grepable comment in case the value changes).
> > + at end itemize
> > +
> > +If all these conditions are satisfied, the filter will notice the user ("Pure
>
> Notify, not notice.
>
Fixed.
> > +channel mapping detected"), and use an optimized and lossless method to do the
> > +remapping.
> > +
> > +For example, if you have a 5.1 source and want a stereo audio stream by
> > +dropping the extra channels:
> > + at example
> > +pan="stereo: c0=FL : c1=FR"
> > + at end example
> > +
> > +Given the same source, you can also switch front left and front right channels
> > +and keep the input channel layout:
> > + at example
> > +pan="5.1: c0=c1 : c1=c0 : c2=c2 : c3=c3 : c4=c4 : c5=c5"
> > + at end example
> > +
> > +If the input is a stereo audio stream, you can mute the front left channel (and
> > +still keep the stereo channel layout) with:
> > + at example
> > +pan="stereo:c1=c1"
> > + at end example
> > +
> > +Still with a stereo audio stream input, you can copy the right channel in both
> > +front left and right:
> > + at example
> > +pan="stereo: c0=FR : c1=FR"
> > + at end example
> > +
> > @section silencedetect
> >
> > Detect silence in an audio stream.
> > diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c
> > index add14b0..37831da 100644
> > --- a/libavfilter/af_pan.c
> > +++ b/libavfilter/af_pan.c
> > @@ -30,6 +30,8 @@
> > #include <stdio.h>
> > #include "libavutil/audioconvert.h"
> > #include "libavutil/avstring.h"
> > +#include "libavutil/opt.h"
> > +#include "libswresample/swresample.h"
> > #include "avfilter.h"
> > #include "internal.h"
> >
> > @@ -46,6 +48,14 @@ typedef struct {
> > int need_renumber;
> > int nb_input_channels;
> > int nb_output_channels;
> > +
> > + int pure_gains;
>
> pure_remap would be more accurate, no?
>
Well I think it's more correct to have a "pure gains" flag: the gains
matrix is pure, and this means there will be a remap (I'd say a remap is
always "pure")
> > + void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *);
> > +
> > + /* channel mapping specific */
> > + int ch[SWR_CH_MAX];
>
> channel_map?
>
Changed.
> > + struct SwrContext *swr;
> > + int sample_rate;
> > } PanContext;
> >
> > static int parse_channel_name(char **arg, int *rchannel, int *rnamed)
> > @@ -93,6 +103,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
> > char *arg, *arg0, *tokenizer, *args = av_strdup(args0);
> > int out_ch_id, in_ch_id, len, named;
> > int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels
> > + int output_ch_has_gain[MAX_CHANNELS] = { 0 };
> > double gain;
> >
> > if (!args0) {
> > @@ -111,6 +122,11 @@ static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
> > }
> > pan->nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channel_layout);
> >
> > + /* assume pure channel re-mapping if the number of output channels is
> > + * supported by libswresample */
> > + if (pan->nb_output_channels < SWR_CH_MAX)
>
> <=
>
> > + pan->pure_gains = 1;
> > +
> > /* parse channel specifications */
> > while ((arg = arg0 = av_strtok(NULL, ":", &tokenizer))) {
> > /* channel name */
> > @@ -162,6 +178,19 @@ static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
> > "Can not mix named and numbered channels\n");
> > return AVERROR(EINVAL);
> > }
> > + /* check if libswresample channel remapping can still be applied */
> > + if (pan->pure_gains) {
> > + /* channel mapping is effective only if 0% or 100% of a channel is
> > + * selected... */
> > + if (gain != 0. && gain != 1.) {
> > + pan->pure_gains = 0;
> > + } else if (gain == 1.) {
> > + /* ...and if the output channel is only composed of one input */
> > + if (output_ch_has_gain[out_ch_id])
> > + pan->pure_gains = 0;
> > + output_ch_has_gain[out_ch_id] = 1;
> > + }
> > + }
>
> I would be much happier if it was completely separated from the parsing
> loop. It would also avoid all the cumbersome boolean variables. Even better
> in a separate function:
>
> static int is_pure_remap(PanContext *pan)
> {
> int i, j, nb_ch;
>
> for (i = 0; i < MAX_CHANNELS; i++) {
> nb_ch = 0;
> for (j = 0; j < MAX_CHANNELS; j++) {
> if (pan->gain.d[i][j]) {
> if (pan->gain.d[i][j] != 1.0)
> return 0;
> if (++nb_ch > 1)
> return 0;
> }
> }
> }
> return 1;
> }
>
Wish granted, with a similar function.
> > pan->gain.d[out_ch_id][in_ch_id] = gain;
> > if (!*arg)
> > break;
> > @@ -179,6 +208,9 @@ static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
> > return 0;
> > }
> >
> > +static void filter_samples_channel_mapping(AVFilterLink *inlink, AVFilterBufferRef *insamples);
> > +static void filter_samples_panning (AVFilterLink *inlink, AVFilterBufferRef *insamples);
> > +
> > static int query_formats(AVFilterContext *ctx)
> > {
> > PanContext *pan = ctx->priv;
> > @@ -186,11 +218,19 @@ static int query_formats(AVFilterContext *ctx)
> > AVFilterLink *outlink = ctx->outputs[0];
> > AVFilterFormats *formats;
> >
> > + if (pan->pure_gains) {
> > + /* libswr supports any sample and packing formats */
> > + avfilter_set_common_sample_formats(ctx, avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO));
> > + avfilter_set_common_packing_formats(ctx, avfilter_make_all_packing_formats());
> > + pan->filter_samples = filter_samples_channel_mapping;
> > + } else {
> > const enum AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S16, -1};
> > const int packing_fmts[] = {AVFILTER_PACKED, -1};
> >
> > avfilter_set_common_sample_formats (ctx, avfilter_make_format_list(sample_fmts));
> > avfilter_set_common_packing_formats(ctx, avfilter_make_format_list(packing_fmts));
> > + pan->filter_samples = filter_samples_panning;
> > + }
> >
> > // inlink supports any channel layout
> > formats = avfilter_make_all_channel_layouts();
> > @@ -222,6 +262,19 @@ static int config_props(AVFilterLink *link)
> > }
> > }
> > }
> > + // gains are pures, init the channel mapping array
>
> Pure, no plural mark.
>
Fixed.
> > + if (pan->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.d[i][j]) {
> > + ch_id = j;
> > + break;
> > + }
> > + }
> > + pan->ch[i] = ch_id;
> > + }
> > + } else {
> > // renormalize
> > for (i = 0; i < pan->nb_output_channels; i++) {
> > if (!((pan->need_renorm >> i) & 1))
> > @@ -239,6 +292,7 @@ static int config_props(AVFilterLink *link)
> > for (j = 0; j < pan->nb_input_channels; j++)
> > pan->gain.d[i][j] /= t;
> > }
> > + }
> > // summary
> > for (i = 0; i < pan->nb_output_channels; i++) {
> > cur = buf;
> > @@ -249,6 +303,15 @@ static int config_props(AVFilterLink *link)
> > }
> > 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->ch[i] < 0) av_log(ctx, AV_LOG_INFO, " M");
> > + else av_log(ctx, AV_LOG_INFO, " %d", pan->ch[i]);
> > + av_log(ctx, AV_LOG_INFO, "\n");
> > + return 0;
> > + }
> > // convert to integer
> > for (i = 0; i < pan->nb_output_channels; i++) {
> > for (j = 0; j < pan->nb_input_channels; j++) {
> > @@ -261,8 +324,37 @@ static int config_props(AVFilterLink *link)
> > return 0;
> > }
> >
> > +static void filter_samples_channel_mapping(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> > +{
> > + int n = insamples->audio->nb_samples;
> > + PanContext *pan = inlink->dst->priv;
> > + AVFilterLink * const outlink = inlink->dst->outputs[0];
>
> The " * " is not coherent with the rest of the style.
>
Fixed.
> > + AVFilterBufferRef *outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, n);
> > + AVFilterBufferRefAudioProps *in = insamples->audio;
> > + AVFilterBufferRefAudioProps *out = outsamples->audio;
> >
> > -static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> > + if (!pan->sample_rate || pan->sample_rate != in->sample_rate) {
>
> Can you explain why it is necessary? In other words, is the sample rate
> supposed to be able to change? And if so, do we really need to take it into
> account: surely, for pure remapping, the sample rate is not relevant?
>
I thought it was possible, but after a few tests it seems that actually it
isn't, so I removed this...
> > + pan->sample_rate = in->sample_rate;
> > + pan->swr = swr_alloc_set_opts(pan->swr,
> > + out->channel_layout, outsamples->format, pan->sample_rate,
> > + in ->channel_layout, insamples ->format, pan->sample_rate,
> > + 0, 0);
> > + if (!pan->swr)
> > + return;
>
> If swr_alloc_set_opts fails, this stops the filter for that round, but for
> the next round, pan->sample_rate will have been set, and the code will skip
> to the call to swr_convert with pan->swr null.
>
> Simple fix: set pan->sample_rate only after everything has succeeded.
>
> Better fix: allocate pan->swr in config_props, and return an explicit error
> if it fails.
>
...which then allowed to do this.
(no explicit error is printed since it should be handled in libswr
correctly, though the error is raised).
>
> > + 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->ch);
> > + if (swr_init(pan->swr) < 0)
> > + return;
>
> Same problem here of course.
>
Also fixed.
> > + }
> > +
> > + swr_convert(pan->swr, outsamples->data, n, insamples->data, n);
> > +
> > + avfilter_filter_samples(outlink, outsamples);
> > + avfilter_unref_buffer(insamples);
> > +}
> > +
> > +static void filter_samples_panning(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> > {
> > PanContext *const pan = inlink->dst->priv;
> > int i, o, n = insamples->audio->nb_samples;
> > @@ -289,6 +381,12 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> > avfilter_unref_buffer(insamples);
> > }
> >
> > +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
> > +{
> > + PanContext * const pan = inlink->dst->priv;
>
> " * " again.
>
All the occurences should be fixed.
> > + pan->filter_samples(inlink, insamples);
> > +}
> > +
>
> It's hard to see in the diff, but I believe some of the code could be
> factored out of filter_samples_*: probably, at least,
> avfilter_get_audio_buffer, avfilter_filter_samples, avfilter_unref_buffer.
>
Good point, refactored.
> > AVFilter avfilter_af_pan = {
> > .name = "pan",
> > .description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)"),
>
> Did you not forget some kind of swr_free?
>
Derp, forgot to merge that chunk from the channel_remap filter... Fixed.
Thank you for your review,
--
Clément B.
-------------- next part --------------
From e886fae70d4fd009b8df2ece35ba11de7f8d17f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <clement.boesch at smartjog.com>
Date: Wed, 18 Jan 2012 12:00:16 +0100
Subject: [PATCH] pan: add channel mapping capability.
---
doc/filters.texi | 45 ++++++++++++++++++
libavfilter/af_pan.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 162 insertions(+), 6 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index 3c9f554..7d008bc 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -315,6 +315,9 @@ Ported from SoX.
Mix channels with specific gain levels. The filter accepts the output
channel layout followed by a set of channels definitions.
+This filter is also designed to remap efficiently the channels of an audio
+stream.
+
The filter accepts parameters of the form:
"@var{l}:@var{outdef}:@var{outdef}:..."
@@ -342,6 +345,8 @@ If the `=' in a channel specification is replaced by `<', then the gains for
that specification will be renormalized so that the total is 1, thus
avoiding clipping noise.
+ at subsection Mixing examples
+
For example, if you want to down-mix from stereo to mono, but with a bigger
factor for the left channel:
@example
@@ -358,6 +363,46 @@ Note that @command{ffmpeg} integrates a default down-mix (and up-mix) system
that should be preferred (see "-ac" option) unless you have very specific
needs.
+ at subsection Remapping examples
+
+The channel remapping will be effective if, and only if:
+
+ at itemize
+ at item gain coefficients are zeroes or ones,
+ at item only one input per channel output,
+ at item the number of output channels is supported by libswresample (16 at the
+ moment)
+ at c if SWR_CH_MAX changes, fix the line above.
+ at end itemize
+
+If all these conditions are satisfied, the filter will notify the user ("Pure
+channel mapping detected"), and use an optimized and lossless method to do the
+remapping.
+
+For example, if you have a 5.1 source and want a stereo audio stream by
+dropping the extra channels:
+ at example
+pan="stereo: c0=FL : c1=FR"
+ at end example
+
+Given the same source, you can also switch front left and front right channels
+and keep the input channel layout:
+ at example
+pan="5.1: c0=c1 : c1=c0 : c2=c2 : c3=c3 : c4=c4 : c5=c5"
+ at end example
+
+If the input is a stereo audio stream, you can mute the front left channel (and
+still keep the stereo channel layout) with:
+ at example
+pan="stereo:c1=c1"
+ at end example
+
+Still with a stereo audio stream input, you can copy the right channel in both
+front left and right:
+ at example
+pan="stereo: c0=FR : c1=FR"
+ at end example
+
@section silencedetect
Detect silence in an audio stream.
diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c
index add14b0..8f4e35c 100644
--- a/libavfilter/af_pan.c
+++ b/libavfilter/af_pan.c
@@ -30,12 +30,14 @@
#include <stdio.h>
#include "libavutil/audioconvert.h"
#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libswresample/swresample.h"
#include "avfilter.h"
#include "internal.h"
#define MAX_CHANNELS 63
-typedef struct {
+typedef struct PanContext {
int64_t out_channel_layout;
union {
double d[MAX_CHANNELS][MAX_CHANNELS];
@@ -46,6 +48,16 @@ typedef struct {
int need_renumber;
int nb_input_channels;
int nb_output_channels;
+
+ int pure_gains;
+ void (*filter_samples)(struct PanContext*,
+ AVFilterBufferRef*,
+ AVFilterBufferRef*,
+ int);
+
+ /* channel mapping specific */
+ int channel_map[SWR_CH_MAX];
+ struct SwrContext *swr;
} PanContext;
static int parse_channel_name(char **arg, int *rchannel, int *rnamed)
@@ -179,6 +191,31 @@ static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
return 0;
}
+static void filter_samples_channel_mapping(PanContext *pan, AVFilterBufferRef *outsamples, AVFilterBufferRef *insamples, int n);
+static void filter_samples_panning (PanContext *pan, AVFilterBufferRef *outsamples, AVFilterBufferRef *insamples, int n);
+
+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.d[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;
@@ -186,11 +223,21 @@ static int query_formats(AVFilterContext *ctx)
AVFilterLink *outlink = ctx->outputs[0];
AVFilterFormats *formats;
+ if (pan->nb_output_channels <= SWR_CH_MAX)
+ pan->pure_gains = are_gains_pure(pan);
+ if (pan->pure_gains) {
+ /* libswr supports any sample and packing formats */
+ avfilter_set_common_sample_formats(ctx, avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO));
+ avfilter_set_common_packing_formats(ctx, avfilter_make_all_packing_formats());
+ pan->filter_samples = filter_samples_channel_mapping;
+ } else {
const enum AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S16, -1};
const int packing_fmts[] = {AVFILTER_PACKED, -1};
avfilter_set_common_sample_formats (ctx, avfilter_make_format_list(sample_fmts));
avfilter_set_common_packing_formats(ctx, avfilter_make_format_list(packing_fmts));
+ pan->filter_samples = filter_samples_panning;
+ }
// inlink supports any channel layout
formats = avfilter_make_all_channel_layouts();
@@ -222,6 +269,34 @@ static int config_props(AVFilterLink *link)
}
}
}
+ // 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.d[i][j]) {
+ ch_id = j;
+ break;
+ }
+ }
+ pan->channel_map[i] = ch_id;
+ }
+
+ // 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, 0);
+ if (!pan->swr)
+ return AVERROR(ENOMEM);
+ 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);
+ r = swr_init(pan->swr);
+ if (r < 0)
+ return r;
+ } else {
// renormalize
for (i = 0; i < pan->nb_output_channels; i++) {
if (!((pan->need_renorm >> i) & 1))
@@ -239,6 +314,7 @@ static int config_props(AVFilterLink *link)
for (j = 0; j < pan->nb_input_channels; j++)
pan->gain.d[i][j] /= t;
}
+ }
// summary
for (i = 0; i < pan->nb_output_channels; i++) {
cur = buf;
@@ -249,6 +325,17 @@ static int config_props(AVFilterLink *link)
}
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;
+ }
// convert to integer
for (i = 0; i < pan->nb_output_channels; i++) {
for (j = 0; j < pan->nb_input_channels; j++) {
@@ -261,19 +348,26 @@ static int config_props(AVFilterLink *link)
return 0;
}
+static void filter_samples_channel_mapping(struct PanContext *pan,
+ AVFilterBufferRef *outsamples,
+ AVFilterBufferRef *insamples,
+ int n)
+{
+ swr_convert(pan->swr, outsamples->data, n, insamples->data, n);
+}
-static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
+static void filter_samples_panning(struct PanContext *pan,
+ AVFilterBufferRef *outsamples,
+ AVFilterBufferRef *insamples,
+ int n)
{
- PanContext *const pan = inlink->dst->priv;
- int i, o, n = insamples->audio->nb_samples;
+ int i, o;
/* input */
const int16_t *in = (int16_t *)insamples->data[0];
const int16_t *in_end = in + n * pan->nb_input_channels;
/* output */
- AVFilterLink *const outlink = inlink->dst->outputs[0];
- AVFilterBufferRef *outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, n);
int16_t *out = (int16_t *)outsamples->data[0];
for (; in < in_end; in += pan->nb_input_channels) {
@@ -284,16 +378,33 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
*(out++) = v >> 8;
}
}
+}
+
+static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
+{
+ int n = insamples->audio->nb_samples;
+ AVFilterLink *const outlink = inlink->dst->outputs[0];
+ AVFilterBufferRef *outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, n);
+ PanContext *pan = inlink->dst->priv;
+
+ pan->filter_samples(pan, outsamples, insamples, n);
avfilter_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[]) {
--
1.7.8.3
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20120118/5527984c/attachment.asc>
More information about the ffmpeg-devel
mailing list