[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