libavfilter/af_aresample.c
3a9e227f
 /*
  * Copyright (c) 2011 Stefano Sabatini
  * Copyright (c) 2011 Mina Nagy Zaki
  *
  * 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
  * resampling audio filter
  */
 
 #include "libavutil/eval.h"
 #include "libavcodec/avcodec.h"
 #include "avfilter.h"
 #include "internal.h"
 
 typedef struct {
     struct AVResampleContext *resample;
     int out_rate;
     double ratio;
     AVFilterBufferRef *outsamplesref;
     int unconsumed_nb_samples,
         max_cached_nb_samples;
     int16_t *cached_data[8],
             *resampled_data[8];
 } AResampleContext;
 
 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
 {
     AResampleContext *aresample = ctx->priv;
     int ret;
 
     if (args) {
         if ((ret = ff_parse_sample_rate(&aresample->out_rate, args, ctx)) < 0)
             return ret;
     } else {
         aresample->out_rate = -1;
     }
 
     return 0;
 }
 
 static av_cold void uninit(AVFilterContext *ctx)
 {
     AResampleContext *aresample = ctx->priv;
     if (aresample->outsamplesref) {
         int nb_channels =
             av_get_channel_layout_nb_channels(
                 aresample->outsamplesref->audio->channel_layout);
         avfilter_unref_buffer(aresample->outsamplesref);
         while (nb_channels--) {
             av_freep(&(aresample->cached_data[nb_channels]));
             av_freep(&(aresample->resampled_data[nb_channels]));
         }
     }
 
     if (aresample->resample)
         av_resample_close(aresample->resample);
 }
 
 static int config_output(AVFilterLink *outlink)
 {
     AVFilterContext *ctx = outlink->src;
     AVFilterLink *inlink = ctx->inputs[0];
     AResampleContext *aresample = ctx->priv;
 
     if (aresample->out_rate == -1)
         aresample->out_rate = outlink->sample_rate;
     else
         outlink->sample_rate = aresample->out_rate;
22137bb5
     outlink->time_base = (AVRational) {1, aresample->out_rate};
3a9e227f
 
     //TODO: make the resampling parameters configurable
     aresample->resample = av_resample_init(aresample->out_rate, inlink->sample_rate,
                                            16, 10, 0, 0.8);
 
     aresample->ratio = (double)outlink->sample_rate / inlink->sample_rate;
 
     av_log(ctx, AV_LOG_INFO, "r:%"PRId64"Hz -> r:%"PRId64"Hz\n",
            inlink->sample_rate, outlink->sample_rate);
     return 0;
 }
 
 static int query_formats(AVFilterContext *ctx)
 {
     AVFilterFormats *formats = NULL;
 
     avfilter_add_format(&formats, AV_SAMPLE_FMT_S16);
     if (!formats)
         return AVERROR(ENOMEM);
     avfilter_set_common_sample_formats(ctx, formats);
 
9899037d
     formats = avfilter_make_all_channel_layouts();
3a9e227f
     if (!formats)
         return AVERROR(ENOMEM);
     avfilter_set_common_channel_layouts(ctx, formats);
 
9899037d
     formats = avfilter_make_all_packing_formats();
3a9e227f
     if (!formats)
         return AVERROR(ENOMEM);
     avfilter_set_common_packing_formats(ctx, formats);
 
     return 0;
 }
 
 static void deinterleave(int16_t **outp, int16_t *in,
                          int nb_channels, int nb_samples)
 {
     int16_t *out[8];
     memcpy(out, outp, nb_channels * sizeof(int16_t*));
 
     switch (nb_channels) {
     case 2:
         while (nb_samples--) {
             *out[0]++ = *in++;
             *out[1]++ = *in++;
         }
         break;
     case 3:
         while (nb_samples--) {
             *out[0]++ = *in++;
             *out[1]++ = *in++;
             *out[2]++ = *in++;
         }
         break;
     case 4:
         while (nb_samples--) {
             *out[0]++ = *in++;
             *out[1]++ = *in++;
             *out[2]++ = *in++;
             *out[3]++ = *in++;
         }
         break;
     case 5:
         while (nb_samples--) {
             *out[0]++ = *in++;
             *out[1]++ = *in++;
             *out[2]++ = *in++;
             *out[3]++ = *in++;
             *out[4]++ = *in++;
         }
         break;
     case 6:
         while (nb_samples--) {
             *out[0]++ = *in++;
             *out[1]++ = *in++;
             *out[2]++ = *in++;
             *out[3]++ = *in++;
             *out[4]++ = *in++;
             *out[5]++ = *in++;
         }
         break;
     case 8:
         while (nb_samples--) {
             *out[0]++ = *in++;
             *out[1]++ = *in++;
             *out[2]++ = *in++;
             *out[3]++ = *in++;
             *out[4]++ = *in++;
             *out[5]++ = *in++;
             *out[6]++ = *in++;
             *out[7]++ = *in++;
         }
         break;
     }
 }
 
 static void interleave(int16_t *out, int16_t **inp,
         int nb_channels, int nb_samples)
 {
     int16_t *in[8];
     memcpy(in, inp, nb_channels * sizeof(int16_t*));
 
     switch (nb_channels) {
     case 2:
         while (nb_samples--) {
             *out++ = *in[0]++;
             *out++ = *in[1]++;
         }
         break;
     case 3:
         while (nb_samples--) {
             *out++ = *in[0]++;
             *out++ = *in[1]++;
             *out++ = *in[2]++;
         }
         break;
     case 4:
         while (nb_samples--) {
             *out++ = *in[0]++;
             *out++ = *in[1]++;
             *out++ = *in[2]++;
             *out++ = *in[3]++;
         }
         break;
     case 5:
         while (nb_samples--) {
             *out++ = *in[0]++;
             *out++ = *in[1]++;
             *out++ = *in[2]++;
             *out++ = *in[3]++;
             *out++ = *in[4]++;
         }
         break;
     case 6:
         while (nb_samples--) {
             *out++ = *in[0]++;
             *out++ = *in[1]++;
             *out++ = *in[2]++;
             *out++ = *in[3]++;
             *out++ = *in[4]++;
             *out++ = *in[5]++;
         }
         break;
     case 8:
         while (nb_samples--) {
             *out++ = *in[0]++;
             *out++ = *in[1]++;
             *out++ = *in[2]++;
             *out++ = *in[3]++;
             *out++ = *in[4]++;
             *out++ = *in[5]++;
             *out++ = *in[6]++;
             *out++ = *in[7]++;
         }
         break;
     }
 }
 
 static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamplesref)
 {
     AResampleContext *aresample  = inlink->dst->priv;
     AVFilterLink * const outlink = inlink->dst->outputs[0];
     int i,
         in_nb_samples            = insamplesref->audio->nb_samples,
         cached_nb_samples        = in_nb_samples + aresample->unconsumed_nb_samples,
         requested_out_nb_samples = aresample->ratio * cached_nb_samples,
         nb_channels              =
             av_get_channel_layout_nb_channels(inlink->channel_layout);
 
     if (cached_nb_samples > aresample->max_cached_nb_samples) {
         for (i = 0; i < nb_channels; i++) {
             aresample->cached_data[i]    =
                 av_realloc(aresample->cached_data[i], cached_nb_samples * sizeof(int16_t));
             aresample->resampled_data[i] =
                 av_realloc(aresample->resampled_data[i],
                            FFALIGN(sizeof(int16_t) * requested_out_nb_samples, 16));
 
             if (aresample->cached_data[i] == NULL || aresample->resampled_data[i] == NULL)
                 return;
         }
         aresample->max_cached_nb_samples = cached_nb_samples;
 
         if (aresample->outsamplesref)
             avfilter_unref_buffer(aresample->outsamplesref);
 
db79d041
         aresample->outsamplesref =
             avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, requested_out_nb_samples);
3a9e227f
         outlink->out_buf = aresample->outsamplesref;
     }
 
22137bb5
     avfilter_copy_buffer_ref_props(aresample->outsamplesref, insamplesref);
     aresample->outsamplesref->audio->sample_rate = outlink->sample_rate;
     aresample->outsamplesref->pts =
         av_rescale(outlink->sample_rate, insamplesref->pts, inlink->sample_rate);
 
3a9e227f
     /* av_resample() works with planar audio buffers */
     if (!inlink->planar && nb_channels > 1) {
         int16_t *out[8];
         for (i = 0; i < nb_channels; i++)
             out[i] = aresample->cached_data[i] + aresample->unconsumed_nb_samples;
 
         deinterleave(out, (int16_t *)insamplesref->data[0],
                      nb_channels, in_nb_samples);
     } else {
         for (i = 0; i < nb_channels; i++)
             memcpy(aresample->cached_data[i] + aresample->unconsumed_nb_samples,
                    insamplesref->data[i],
                    in_nb_samples * sizeof(int16_t));
     }
 
     for (i = 0; i < nb_channels; i++) {
         int consumed_nb_samples;
         const int is_last = i+1 == nb_channels;
 
         aresample->outsamplesref->audio->nb_samples =
             av_resample(aresample->resample,
                         aresample->resampled_data[i], aresample->cached_data[i],
                         &consumed_nb_samples,
                         cached_nb_samples,
                         requested_out_nb_samples, is_last);
 
         /* move unconsumed data back to the beginning of the cache */
         aresample->unconsumed_nb_samples = cached_nb_samples - consumed_nb_samples;
         memmove(aresample->cached_data[i],
                 aresample->cached_data[i] + consumed_nb_samples,
                 aresample->unconsumed_nb_samples * sizeof(int16_t));
     }
 
 
     /* copy resampled data to the output samplesref */
     if (!inlink->planar && nb_channels > 1) {
         interleave((int16_t *)aresample->outsamplesref->data[0],
                    aresample->resampled_data,
                    nb_channels, aresample->outsamplesref->audio->nb_samples);
     } else {
         for (i = 0; i < nb_channels; i++)
             memcpy(aresample->outsamplesref->data[i], aresample->resampled_data[i],
                    aresample->outsamplesref->audio->nb_samples * sizeof(int16_t));
     }
 
     avfilter_filter_samples(outlink, avfilter_ref_buffer(aresample->outsamplesref, ~0));
     avfilter_unref_buffer(insamplesref);
 }
 
 AVFilter avfilter_af_aresample = {
     .name          = "aresample",
     .description   = NULL_IF_CONFIG_SMALL("Resample audio data."),
     .init          = init,
     .uninit        = uninit,
     .query_formats = query_formats,
     .priv_size     = sizeof(AResampleContext),
 
5af7daab
     .inputs    = (const AVFilterPad[]) {{ .name      = "default",
3a9e227f
                                     .type            = AVMEDIA_TYPE_AUDIO,
                                     .filter_samples  = filter_samples,
                                     .min_perms       = AV_PERM_READ, },
                                   { .name = NULL}},
5af7daab
     .outputs   = (const AVFilterPad[]) {{ .name      = "default",
3a9e227f
                                     .config_props    = config_output,
                                     .type            = AVMEDIA_TYPE_AUDIO, },
                                   { .name = NULL}},
 };