[FFmpeg-devel] [PATCH] afade filter
Paul B Mahol
onemda at gmail.com
Sat Jan 19 20:15:28 CET 2013
On 1/19/13, Nicolas George <nicolas.george at normalesup.org> wrote:
> Le decadi 30 nivose, an CCXXI, Paul B Mahol a ecrit :
>> Signed-off-by: Paul B Mahol <onemda at gmail.com>
>> ---
>> doc/filters.texi | 63 ++++++++++
>> libavfilter/Makefile | 1 +
>> libavfilter/af_afade.c | 316
>> +++++++++++++++++++++++++++++++++++++++++++++++
>> libavfilter/allfilters.c | 1 +
>> 4 files changed, 381 insertions(+)
>> create mode 100644 libavfilter/af_afade.c
>
> Nice.
>
>>
>> diff --git a/doc/filters.texi b/doc/filters.texi
>> index 42c78b8..806ff48 100644
>> --- a/doc/filters.texi
>> +++ b/doc/filters.texi
>> @@ -282,6 +282,69 @@ aconvert=u8:auto
>> @end example
>> @end itemize
>>
>> + at section afade
>> +
>> +Apply fade-in/out effect to input audio.
>> +
>> +The filter accepts parameters as a list of @var{key}=@var{value}
>> +pairs, separated by ":".
>> +
>> +A description of the accepted parameters follows.
>> +
>> + at table @option
>> + at item type, t
>> +Specify the effect type, can be either @code{in} for fade-in, or
>> + at code{out} for a fade-out effect. Default is @code{in}.
>> +
>
>> + at item start_sample, s
>
> Can we keep the short "s" option for if someone implements it in seconds
> instead of samples? I feel that it would be more user friendly.
Yes, changed locally to st.
>
[...]
>> + if ((ret = av_set_options_string(afade, args, "=", ":")) < 0)
>> + return ret;
>> +
>
>> + if (INT64_MAX - afade->nb_samples < afade->start_sample)
>> + return AVERROR(EINVAL);
>
> Error message?
Maybe but it is overkill.
>
>> +
>> + return 0;
>> +}
>> +
>> +static double fade_gain(int curve, int index, int range)
>> +{
>> + double gain;
>> +
>> + gain = FFMAX(0.0, FFMIN(1.0, 1.0 * index / range));
>> +
>> + switch (curve) {
>> + case QSIN:
>> + gain = sin(gain * M_PI / 2.0);
>> + break;
>> + case HSIN:
>> + gain = (1.0 - cos(gain * M_PI)) / 2.0;
>> + break;
>> + case LOG:
>> + gain = pow(0.1, (1 - gain) * 5.0);
>> + break;
>> + case PAR:
>> + gain = (1 - (1 - gain) * (1 - gain));
>> + break;
>> + case QUA:
>> + gain *= gain;
>> + break;
>> + case CUB:
>> + gain = gain * gain * gain;
>> + break;
>> + case SQU:
>> + gain = sqrt(gain);
>> + break;
>> + case CBR:
>> + gain = cbrt(gain);
>> + break;
>> + }
>> +
>> + return gain;
>> +}
>
> I wonder whether a function pointer would be more efficient than a switch.
> But it probably does not matter much.
>
>> +
>> +static void fade_samples_u8(uint8_t **dst, uint8_t **src,
>> + int nb_samples, int planes,
>> + int64_t start, int fade_range, int curve)
>> +{
>> + int i, p;
>> +
>> + for (p = 0; p < planes; p++) {
>> + uint8_t *d = dst[p];
>> + const uint8_t *s = src[p];
>> +
>> + for (i = 0; i < nb_samples; i++) {
>> + d[i] = av_clip_uint8(((int64_t)s[i] - 128) *
>> + fade_gain(curve, start + i, fade_range) + 128);
>> + }
>> + }
>> +}
>> +
>> +static void fade_samples_s16(uint8_t **dst, uint8_t **src,
>> + int nb_samples, int planes,
>> + int64_t start, int fade_range, int curve)
>> +{
>> + int i, p;
>> +
>> + for (p = 0; p < planes; p++) {
>> + int16_t *d = (int16_t *)dst[p];
>> + const int16_t *s = (int16_t *)src[p];
>> +
>> + for (i = 0; i < nb_samples; i++) {
>> + d[i] = s[i] * fade_gain(curve, start + i, fade_range);
>> + }
>> + }
>> +}
>> +
>> +static void fade_samples_s32(uint8_t **dst, uint8_t **src,
>> + int nb_samples, int planes,
>> + int64_t start, int fade_range, int curve)
>> +{
>> + int i, p;
>> +
>> + for (p = 0; p < planes; p++) {
>> + int32_t *d = (int32_t *)dst[p];
>> + const int32_t *s = (int32_t *)src[p];
>> +
>> + for (i = 0; i < nb_samples; i++) {
>> + d[i] = s[i] * fade_gain(curve, start + i, fade_range);
>> + }
>> + }
>> +}
>> +
>> +static void fade_samples_flt(uint8_t **dst, uint8_t **src,
>> + int nb_samples, int planes,
>> + int64_t start, int fade_range, int curve)
>> +{
>> + int i, p;
>> +
>> + for (p = 0; p < planes; p++) {
>> + float *d = (float *)dst[p];
>> + const float *s = (float *)src[p];
>> +
>> + for (i = 0; i < nb_samples; i++) {
>> + d[i] = s[i] * fade_gain(curve, start + i, fade_range);
>> + }
>> + }
>> +}
>> +
>> +static void fade_samples_dbl(uint8_t **dst, uint8_t **src,
>> + int nb_samples, int planes,
>> + int64_t start, int fade_range, int curve)
>> +{
>> + int i, p;
>> +
>> + for (p = 0; p < planes; p++) {
>> + double *d = (double *)dst[p];
>> + const double *s = (double *)src[p];
>> +
>> + for (i = 0; i < nb_samples; i++) {
>> + d[i] = s[i] * fade_gain(curve, start + i, fade_range);
>> + }
>> + }
>> +}
>
> A macro could probably allow to define the five functions with much less
> lines of code.
Yes. I will hack something...
>
>> +
>> +static int config_output(AVFilterLink *outlink)
>> +{
>> + AVFilterContext *ctx = outlink->src;
>> + AudioFadeContext *afade = ctx->priv;
>> + AVFilterLink *inlink = ctx->inputs[0];
>> +
>> + switch (av_get_packed_sample_fmt(inlink->format)) {
>> + case AV_SAMPLE_FMT_U8:
>> + afade->fade_samples = fade_samples_u8;
>> + break;
>> + case AV_SAMPLE_FMT_S16:
>> + afade->fade_samples = fade_samples_s16;
>> + break;
>> + case AV_SAMPLE_FMT_S32:
>> + afade->fade_samples = fade_samples_s32;
>> + break;
>> + case AV_SAMPLE_FMT_FLT:
>> + afade->fade_samples = fade_samples_flt;
>> + break;
>> + case AV_SAMPLE_FMT_DBL:
>> + afade->fade_samples = fade_samples_dbl;
>> + break;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
>> +{
>> + AudioFadeContext *afade = inlink->dst->priv;
>> + AVFilterLink *outlink = inlink->dst->outputs[0];
>> + int nb_samples = buf->audio->nb_samples;
>> + AVFilterBufferRef *out_buf;
>> + int planes = av_sample_fmt_is_planar(buf->format) ?
>> buf->audio->channels : 1;
>> + int plane_samples = planes > 1 ? nb_samples : nb_samples *
>> buf->audio->channels;
>> + int64_t cur_sample = av_rescale_q(buf->pts, (AVRational){1,
>> outlink->sample_rate}, outlink->time_base);
>> +
>> + if ((!afade->type && (afade->start_sample + afade->nb_samples <
>> cur_sample)) ||
>> + ( afade->type && (cur_sample + afade->nb_samples <
>> afade->start_sample)))
>> + return ff_filter_frame(outlink, buf);
>> +
>
>> + if (buf->perms & AV_PERM_WRITE) {
>> + out_buf = buf;
>> + } else {
>> + out_buf = ff_get_audio_buffer(inlink, AV_PERM_WRITE,
>> nb_samples);
>> + if (!out_buf)
>> + return AVERROR(ENOMEM);
>> + out_buf->pts = buf->pts;
>> + }
>
> Did you test the case where AV_PERM_WRITE is missing? At first glance, it
> looks like it is missing the copy of the unmodified original sample.
It works fine, I could add av_samples_copy() to just copy samples that are
otherwise currently multiplied with 1.0 and unchanged.
[...]
More information about the ffmpeg-devel
mailing list