1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

avfilter: add acrossfade filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
Paul B Mahol 2015-07-23 22:13:17 +00:00
parent 0b6f092ed3
commit 4a2836eaf3
6 changed files with 462 additions and 57 deletions

View File

@ -26,6 +26,7 @@ version <next>:
- AAC fixed-point decoding
- sidechaincompress audio filter
- bitstream filter for converting HEVC from MP4 to Annex B
- acrossfade audio filter
version 2.7:

View File

@ -318,6 +318,54 @@ build.
Below is a description of the currently available audio filters.
@section acrossfade
Apply cross fade from one input audio stream to another input audio stream.
The cross fade is applied for specified duration near the end of first stream.
The filter accepts the following options:
@table @option
@item nb_samples, ns
Specify the number of samples for which the cross fade effect has to last.
At the end of the cross fade effect the first input audio will be completely
silent. Default is 44100.
@item duration, d
Specify the duration of the cross fade effect. See
@ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils}
for the accepted syntax.
By default the duration is determined by @var{nb_samples}.
If set this option is used instead of @var{nb_samples}.
@item overlap, o
Should first stream end overlap with second stream start. Default is enabled.
@item curve1
Set curve for cross fade transition for first stream.
@item curve2
Set curve for cross fade transition for second stream.
For description of available curve types see @ref{afade} filter description.
@end table
@subsection Examples
@itemize
@item
Cross fade from one input to another:
@example
ffmpeg -i first.flac -i second.flac -filter_complex acrossfade=d=10:c1=exp:c2=exp output.flac
@end example
@item
Cross fade from one input to another but without overlapping:
@example
ffmpeg -i first.flac -i second.flac -filter_complex acrossfade=d=10:o=0:c1=exp:c2=exp output.flac
@end example
@end itemize
@section adelay
Delay one or more audio channels.
@ -469,6 +517,7 @@ aeval=val(0)|-val(1)
@end example
@end itemize
@anchor{afade}
@section afade
Apply fade-in/out effect to input audio.

View File

@ -29,6 +29,7 @@ OBJS = allfilters.o \
OBJS-$(CONFIG_AVCODEC) += avcodec.o
OBJS-$(CONFIG_ACROSSFADE_FILTER) += af_afade.o
OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o
OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o
OBJS-$(CONFIG_AEVAL_FILTER) += aeval.o

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Paul B Mahol
* Copyright (c) 2013-2015 Paul B Mahol
*
* This file is part of FFmpeg.
*
@ -23,6 +23,7 @@
* fade audio filter
*/
#include "libavutil/audio_fifo.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
@ -31,15 +32,24 @@
typedef struct {
const AVClass *class;
int type;
int curve;
int curve, curve2;
int nb_samples;
int64_t start_sample;
int64_t duration;
int64_t start_time;
int overlap;
int cf0_eof;
int crossfade_is_over;
AVAudioFifo *fifo[2];
int64_t pts;
void (*fade_samples)(uint8_t **dst, uint8_t * const *src,
int nb_samples, int channels, int direction,
int64_t start, int range, int curve);
void (*crossfade_samples)(uint8_t **dst, uint8_t * const *cf0,
uint8_t * const *cf1,
int nb_samples, int channels,
int curve0, int curve1);
} AudioFadeContext;
enum CurveType { TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP, IQSIN, IHSIN, DESE, DESI, NB_CURVES };
@ -47,52 +57,6 @@ enum CurveType { TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP,
#define OFFSET(x) offsetof(AudioFadeContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption afade_options[] = {
{ "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "type" },
{ "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "type" },
{ "in", "fade-in", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "type" },
{ "out", "fade-out", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "type" },
{ "start_sample", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, FLAGS },
{ "ss", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, FLAGS },
{ "nb_samples", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX, FLAGS },
{ "ns", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX, FLAGS },
{ "start_time", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "st", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "duration", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "d", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "curve", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve" },
{ "c", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve" },
{ "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, "curve" },
{ "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, "curve" },
{ "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, "curve" },
{ "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, "curve" },
{ "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, "curve" },
{ "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, FLAGS, "curve" },
{ "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, "curve" },
{ "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, "curve" },
{ "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, "curve" },
{ "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, "curve" },
{ "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, "curve" },
{ "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, FLAGS, "curve" },
{ "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, FLAGS, "curve" },
{ "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, FLAGS, "curve" },
{ "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, FLAGS, "curve" },
{ "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, FLAGS, "curve" },
{ NULL }
};
AVFILTER_DEFINE_CLASS(afade);
static av_cold int init(AVFilterContext *ctx)
{
AudioFadeContext *s = ctx->priv;
if (INT64_MAX - s->nb_samples < s->start_sample)
return AVERROR(EINVAL);
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats;
@ -227,12 +191,12 @@ FADE(flt, float)
FADE(s16, int16_t)
FADE(s32, int32_t)
static int config_input(AVFilterLink *inlink)
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = inlink->dst;
AVFilterContext *ctx = outlink->src;
AudioFadeContext *s = ctx->priv;
switch (inlink->format) {
switch (outlink->format) {
case AV_SAMPLE_FMT_DBL: s->fade_samples = fade_samples_dbl; break;
case AV_SAMPLE_FMT_DBLP: s->fade_samples = fade_samples_dblp; break;
case AV_SAMPLE_FMT_FLT: s->fade_samples = fade_samples_flt; break;
@ -244,9 +208,57 @@ static int config_input(AVFilterLink *inlink)
}
if (s->duration)
s->nb_samples = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE);
s->nb_samples = av_rescale(s->duration, outlink->sample_rate, AV_TIME_BASE);
if (s->start_time)
s->start_sample = av_rescale(s->start_time, inlink->sample_rate, AV_TIME_BASE);
s->start_sample = av_rescale(s->start_time, outlink->sample_rate, AV_TIME_BASE);
return 0;
}
#if CONFIG_AFADE_FILTER
static const AVOption afade_options[] = {
{ "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "type" },
{ "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "type" },
{ "in", "fade-in", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "type" },
{ "out", "fade-out", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "type" },
{ "start_sample", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, FLAGS },
{ "ss", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, FLAGS },
{ "nb_samples", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX, FLAGS },
{ "ns", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX, FLAGS },
{ "start_time", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "st", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "duration", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "d", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "curve", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve" },
{ "c", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve" },
{ "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, "curve" },
{ "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, "curve" },
{ "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, "curve" },
{ "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, "curve" },
{ "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, "curve" },
{ "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, FLAGS, "curve" },
{ "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, "curve" },
{ "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, "curve" },
{ "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, "curve" },
{ "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, "curve" },
{ "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, "curve" },
{ "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, FLAGS, "curve" },
{ "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, FLAGS, "curve" },
{ "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, FLAGS, "curve" },
{ "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, FLAGS, "curve" },
{ "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, FLAGS, "curve" },
{ NULL }
};
AVFILTER_DEFINE_CLASS(afade);
static av_cold int init(AVFilterContext *ctx)
{
AudioFadeContext *s = ctx->priv;
if (INT64_MAX - s->nb_samples < s->start_sample)
return AVERROR(EINVAL);
return 0;
}
@ -301,15 +313,15 @@ static const AVFilterPad avfilter_af_afade_inputs[] = {
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
.config_props = config_input,
},
{ NULL }
};
static const AVFilterPad avfilter_af_afade_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
},
{ NULL }
};
@ -325,3 +337,344 @@ AVFilter ff_af_afade = {
.priv_class = &afade_class,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
};
#endif /* CONFIG_AFADE_FILTER */
#if CONFIG_ACROSSFADE_FILTER
static const AVOption acrossfade_options[] = {
{ "nb_samples", "set number of samples for cross fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX/10, FLAGS },
{ "ns", "set number of samples for cross fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX/10, FLAGS },
{ "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, 60, FLAGS },
{ "d", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, 60, FLAGS },
{ "overlap", "overlap 1st stream end with 2nd stream start", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 1, FLAGS },
{ "o", "overlap 1st stream end with 2nd stream start", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 1, FLAGS },
{ "curve1", "set fade curve type for 1st stream", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve1" },
{ "c1", "set fade curve type for 1st stream", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve1" },
{ "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, "curve1" },
{ "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, "curve1" },
{ "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, "curve1" },
{ "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, "curve1" },
{ "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, "curve1" },
{ "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, FLAGS, "curve1" },
{ "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, "curve1" },
{ "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, "curve1" },
{ "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, "curve1" },
{ "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, "curve1" },
{ "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, "curve1" },
{ "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, FLAGS, "curve1" },
{ "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, FLAGS, "curve1" },
{ "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, FLAGS, "curve1" },
{ "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, FLAGS, "curve1" },
{ "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, FLAGS, "curve1" },
{ "curve2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve2" },
{ "c2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, 0, NB_CURVES - 1, FLAGS, "curve2" },
{ "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, "curve2" },
{ "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, "curve2" },
{ "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, "curve2" },
{ "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, "curve2" },
{ "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, "curve2" },
{ "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, FLAGS, "curve2" },
{ "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, "curve2" },
{ "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, "curve2" },
{ "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, "curve2" },
{ "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, "curve2" },
{ "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, "curve2" },
{ "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, FLAGS, "curve2" },
{ "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, FLAGS, "curve2" },
{ "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, FLAGS, "curve2" },
{ "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, FLAGS, "curve2" },
{ "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, FLAGS, "curve2" },
{ NULL }
};
AVFILTER_DEFINE_CLASS(acrossfade);
#define CROSSFADE_PLANAR(name, type) \
static void crossfade_samples_## name ##p(uint8_t **dst, uint8_t * const *cf0, \
uint8_t * const *cf1, \
int nb_samples, int channels, \
int curve0, int curve1) \
{ \
int i, c; \
\
for (i = 0; i < nb_samples; i++) { \
double gain0 = fade_gain(curve0, nb_samples - 1 - i, nb_samples); \
double gain1 = fade_gain(curve1, i, nb_samples); \
for (c = 0; c < channels; c++) { \
type *d = (type *)dst[c]; \
const type *s0 = (type *)cf0[c]; \
const type *s1 = (type *)cf1[c]; \
\
d[i] = s0[i] * gain0 + s1[i] * gain1; \
} \
} \
}
#define CROSSFADE(name, type) \
static void crossfade_samples_## name (uint8_t **dst, uint8_t * const *cf0, \
uint8_t * const *cf1, \
int nb_samples, int channels, \
int curve0, int curve1) \
{ \
type *d = (type *)dst[0]; \
const type *s0 = (type *)cf0[0]; \
const type *s1 = (type *)cf1[0]; \
int i, c, k = 0; \
\
for (i = 0; i < nb_samples; i++) { \
double gain0 = fade_gain(curve0, nb_samples - 1 - i, nb_samples); \
double gain1 = fade_gain(curve1, i, nb_samples); \
for (c = 0; c < channels; c++, k++) \
d[k] = s0[k] * gain0 + s1[k] * gain1; \
} \
}
CROSSFADE_PLANAR(dbl, double)
CROSSFADE_PLANAR(flt, float)
CROSSFADE_PLANAR(s16, int16_t)
CROSSFADE_PLANAR(s32, int32_t)
CROSSFADE(dbl, double)
CROSSFADE(flt, float)
CROSSFADE(s16, int16_t)
CROSSFADE(s32, int32_t)
static int acrossfade_filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
AudioFadeContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
AVFrame *out, *cf[2] = { NULL };
int ret = 0, nb_samples;
if (s->crossfade_is_over) {
in->pts = s->pts;
s->pts += av_rescale_q(in->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
return ff_filter_frame(outlink, in);
} else if (inlink == ctx->inputs[0]) {
av_audio_fifo_write(s->fifo[0], (void **)in->extended_data, in->nb_samples);
nb_samples = av_audio_fifo_size(s->fifo[0]) - s->nb_samples;
if (nb_samples > 0) {
out = ff_get_audio_buffer(outlink, nb_samples);
if (!out) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_audio_fifo_read(s->fifo[0], (void **)out->extended_data, nb_samples);
out->pts = s->pts;
s->pts += av_rescale_q(nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
ret = ff_filter_frame(outlink, out);
}
} else if (av_audio_fifo_size(s->fifo[1]) < s->nb_samples) {
if (!s->overlap && av_audio_fifo_size(s->fifo[0]) > 0) {
nb_samples = av_audio_fifo_size(s->fifo[0]);
cf[0] = ff_get_audio_buffer(outlink, nb_samples);
out = ff_get_audio_buffer(outlink, nb_samples);
if (!out || !cf[0]) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_audio_fifo_read(s->fifo[0], (void **)cf[0]->extended_data, nb_samples);
s->fade_samples(out->extended_data, cf[0]->extended_data, nb_samples,
outlink->channels, -1, nb_samples - 1, nb_samples, s->curve);
out->pts = s->pts;
s->pts += av_rescale_q(nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
ret = ff_filter_frame(outlink, out);
if (ret < 0)
goto fail;
}
av_audio_fifo_write(s->fifo[1], (void **)in->extended_data, in->nb_samples);
} else if (av_audio_fifo_size(s->fifo[1]) >= s->nb_samples) {
if (s->overlap) {
cf[0] = ff_get_audio_buffer(outlink, s->nb_samples);
cf[1] = ff_get_audio_buffer(outlink, s->nb_samples);
out = ff_get_audio_buffer(outlink, s->nb_samples);
if (!out || !cf[0] || !cf[1]) {
av_frame_free(&out);
ret = AVERROR(ENOMEM);
goto fail;
}
av_audio_fifo_read(s->fifo[0], (void **)cf[0]->extended_data, s->nb_samples);
av_audio_fifo_read(s->fifo[1], (void **)cf[1]->extended_data, s->nb_samples);
s->crossfade_samples(out->extended_data, cf[0]->extended_data,
cf[1]->extended_data,
s->nb_samples, av_frame_get_channels(in),
s->curve, s->curve2);
out->pts = s->pts;
s->pts += av_rescale_q(s->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
ret = ff_filter_frame(outlink, out);
if (ret < 0)
goto fail;
} else {
out = ff_get_audio_buffer(outlink, s->nb_samples);
cf[1] = ff_get_audio_buffer(outlink, s->nb_samples);
if (!out || !cf[1]) {
ret = AVERROR(ENOMEM);
av_frame_free(&out);
goto fail;
}
av_audio_fifo_read(s->fifo[1], (void **)cf[1]->extended_data, s->nb_samples);
s->fade_samples(out->extended_data, cf[1]->extended_data, s->nb_samples,
outlink->channels, 1, 0, s->nb_samples, s->curve2);
out->pts = s->pts;
s->pts += av_rescale_q(s->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
ret = ff_filter_frame(outlink, out);
if (ret < 0)
goto fail;
}
nb_samples = av_audio_fifo_size(s->fifo[1]);
if (nb_samples > 0) {
out = ff_get_audio_buffer(outlink, nb_samples);
if (!out) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_audio_fifo_read(s->fifo[1], (void **)out->extended_data, nb_samples);
out->pts = s->pts;
s->pts += av_rescale_q(nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
ret = ff_filter_frame(outlink, out);
}
s->crossfade_is_over = 1;
}
fail:
av_frame_free(&in);
av_frame_free(&cf[0]);
av_frame_free(&cf[1]);
return ret;
}
static int acrossfade_request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AudioFadeContext *s = ctx->priv;
int ret = 0;
if (!s->cf0_eof) {
AVFilterLink *cf0 = ctx->inputs[0];
ret = ff_request_frame(cf0);
if (ret < 0 && ret != AVERROR_EOF)
return ret;
if (ret == AVERROR_EOF) {
s->cf0_eof = 1;
ret = 0;
}
} else {
AVFilterLink *cf1 = ctx->inputs[1];
int nb_samples = av_audio_fifo_size(s->fifo[1]);
ret = ff_request_frame(cf1);
if (ret == AVERROR_EOF && nb_samples > 0) {
AVFrame *out = ff_get_audio_buffer(outlink, nb_samples);
if (!out)
return AVERROR(ENOMEM);
av_audio_fifo_read(s->fifo[1], (void **)out->extended_data, nb_samples);
ret = ff_filter_frame(outlink, out);
}
}
return ret;
}
static int acrossfade_config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AudioFadeContext *s = ctx->priv;
if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) {
av_log(ctx, AV_LOG_ERROR,
"Inputs must have the same sample rate "
"%d for in0 vs %d for in1\n",
ctx->inputs[0]->sample_rate, ctx->inputs[1]->sample_rate);
return AVERROR(EINVAL);
}
outlink->sample_rate = ctx->inputs[0]->sample_rate;
outlink->time_base = ctx->inputs[0]->time_base;
outlink->channel_layout = ctx->inputs[0]->channel_layout;
outlink->channels = ctx->inputs[0]->channels;
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
switch (outlink->format) {
case AV_SAMPLE_FMT_DBL: s->crossfade_samples = crossfade_samples_dbl; break;
case AV_SAMPLE_FMT_DBLP: s->crossfade_samples = crossfade_samples_dblp; break;
case AV_SAMPLE_FMT_FLT: s->crossfade_samples = crossfade_samples_flt; break;
case AV_SAMPLE_FMT_FLTP: s->crossfade_samples = crossfade_samples_fltp; break;
case AV_SAMPLE_FMT_S16: s->crossfade_samples = crossfade_samples_s16; break;
case AV_SAMPLE_FMT_S16P: s->crossfade_samples = crossfade_samples_s16p; break;
case AV_SAMPLE_FMT_S32: s->crossfade_samples = crossfade_samples_s32; break;
case AV_SAMPLE_FMT_S32P: s->crossfade_samples = crossfade_samples_s32p; break;
}
config_output(outlink);
s->fifo[0] = av_audio_fifo_alloc(outlink->format, outlink->channels, s->nb_samples);
s->fifo[1] = av_audio_fifo_alloc(outlink->format, outlink->channels, s->nb_samples);
if (!s->fifo[0] || !s->fifo[1])
return AVERROR(ENOMEM);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
AudioFadeContext *s = ctx->priv;
av_audio_fifo_free(s->fifo[0]);
av_audio_fifo_free(s->fifo[1]);
}
static const AVFilterPad avfilter_af_acrossfade_inputs[] = {
{
.name = "crossfade0",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = acrossfade_filter_frame,
},
{
.name = "crossfade1",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = acrossfade_filter_frame,
},
{ NULL }
};
static const AVFilterPad avfilter_af_acrossfade_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.request_frame = acrossfade_request_frame,
.config_props = acrossfade_config_output,
},
{ NULL }
};
AVFilter ff_af_acrossfade = {
.name = "acrossfade",
.description = NULL_IF_CONFIG_SMALL("Cross fade two input audio streams."),
.query_formats = query_formats,
.priv_size = sizeof(AudioFadeContext),
.uninit = uninit,
.priv_class = &acrossfade_class,
.inputs = avfilter_af_acrossfade_inputs,
.outputs = avfilter_af_acrossfade_outputs,
};
#endif /* CONFIG_ACROSSFADE_FILTER */

View File

@ -45,6 +45,7 @@ void avfilter_register_all(void)
return;
initialized = 1;
REGISTER_FILTER(ACROSSFADE, acrossfade, af);
REGISTER_FILTER(ADELAY, adelay, af);
REGISTER_FILTER(AECHO, aecho, af);
REGISTER_FILTER(AEVAL, aeval, af);

View File

@ -30,7 +30,7 @@
#include "libavutil/version.h"
#define LIBAVFILTER_VERSION_MAJOR 5
#define LIBAVFILTER_VERSION_MINOR 29
#define LIBAVFILTER_VERSION_MINOR 30
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \