You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	lavfi: add aresample filter
Based on a patch by Stefano. Signed-off-by: Stefano Sabatini <stefano.sabatini-lala@poste.it>
This commit is contained in:
		
				
					committed by
					
						 Stefano Sabatini
						Stefano Sabatini
					
				
			
			
				
	
			
			
			
						parent
						
							7c94740b84
						
					
				
				
					commit
					3a9e227fb1
				
			| @@ -124,6 +124,19 @@ aformat=s16:mono\\,stereo:all | |||||||
|  |  | ||||||
| Pass the audio source unchanged to the output. | Pass the audio source unchanged to the output. | ||||||
|  |  | ||||||
|  | @section aresample | ||||||
|  |  | ||||||
|  | Resample the input audio to the specified sample rate. | ||||||
|  |  | ||||||
|  | The filter accepts exactly one parameter, the output sample rate. If not | ||||||
|  | specified then the filter will automatically convert between its input | ||||||
|  | and output sample rates. | ||||||
|  |  | ||||||
|  | For example, to resample the input audio to 44100Hz: | ||||||
|  | @example | ||||||
|  | aresample=44100 | ||||||
|  | @end example | ||||||
|  |  | ||||||
| @c man end AUDIO FILTERS | @c man end AUDIO FILTERS | ||||||
|  |  | ||||||
| @chapter Audio Sources | @chapter Audio Sources | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ include $(SUBDIR)../config.mak | |||||||
|  |  | ||||||
| NAME = avfilter | NAME = avfilter | ||||||
| FFLIBS = avutil | FFLIBS = avutil | ||||||
|  | FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += avcodec | ||||||
| FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec | FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec | ||||||
| FFLIBS-$(CONFIG_SCALE_FILTER) += swscale | FFLIBS-$(CONFIG_SCALE_FILTER) += swscale | ||||||
| FFLIBS-$(CONFIG_MP_FILTER) += avcodec | FFLIBS-$(CONFIG_MP_FILTER) += avcodec | ||||||
| @@ -20,6 +21,7 @@ OBJS-$(CONFIG_AVCODEC)                       += avcodec.o | |||||||
|  |  | ||||||
| OBJS-$(CONFIG_AFORMAT_FILTER)                += af_aformat.o | OBJS-$(CONFIG_AFORMAT_FILTER)                += af_aformat.o | ||||||
| OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o | OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o | ||||||
|  | OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.o | ||||||
|  |  | ||||||
| OBJS-$(CONFIG_ANULLSRC_FILTER)               += asrc_anullsrc.o | OBJS-$(CONFIG_ANULLSRC_FILTER)               += asrc_anullsrc.o | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										351
									
								
								libavfilter/af_aresample.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								libavfilter/af_aresample.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,351 @@ | |||||||
|  | /* | ||||||
|  |  * 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; | ||||||
|  |  | ||||||
|  |     //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); | ||||||
|  |  | ||||||
|  |     formats = avfilter_all_channel_layouts(); | ||||||
|  |     if (!formats) | ||||||
|  |         return AVERROR(ENOMEM); | ||||||
|  |     avfilter_set_common_channel_layouts(ctx, formats); | ||||||
|  |  | ||||||
|  |     formats = avfilter_all_packing_formats(); | ||||||
|  |     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); | ||||||
|  |  | ||||||
|  |         aresample->outsamplesref = avfilter_get_audio_buffer(outlink, | ||||||
|  |                                                             AV_PERM_WRITE | AV_PERM_REUSE2, | ||||||
|  |                                                             inlink->format, | ||||||
|  |                                                             requested_out_nb_samples, | ||||||
|  |                                                             insamplesref->audio->channel_layout, | ||||||
|  |                                                             insamplesref->audio->planar); | ||||||
|  |  | ||||||
|  |         avfilter_copy_buffer_ref_props(aresample->outsamplesref, insamplesref); | ||||||
|  |         aresample->outsamplesref->pts = | ||||||
|  |             insamplesref->pts / inlink->sample_rate * outlink->sample_rate; | ||||||
|  |         aresample->outsamplesref->audio->sample_rate = outlink->sample_rate; | ||||||
|  |         outlink->out_buf = aresample->outsamplesref; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* 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), | ||||||
|  |  | ||||||
|  |     .inputs    = (AVFilterPad[]) {{ .name            = "default", | ||||||
|  |                                     .type            = AVMEDIA_TYPE_AUDIO, | ||||||
|  |                                     .filter_samples  = filter_samples, | ||||||
|  |                                     .min_perms       = AV_PERM_READ, }, | ||||||
|  |                                   { .name = NULL}}, | ||||||
|  |     .outputs   = (AVFilterPad[]) {{ .name            = "default", | ||||||
|  |                                     .config_props    = config_output, | ||||||
|  |                                     .type            = AVMEDIA_TYPE_AUDIO, }, | ||||||
|  |                                   { .name = NULL}}, | ||||||
|  | }; | ||||||
| @@ -36,6 +36,7 @@ void avfilter_register_all(void) | |||||||
|  |  | ||||||
|     REGISTER_FILTER (AFORMAT,     aformat,     af); |     REGISTER_FILTER (AFORMAT,     aformat,     af); | ||||||
|     REGISTER_FILTER (ANULL,       anull,       af); |     REGISTER_FILTER (ANULL,       anull,       af); | ||||||
|  |     REGISTER_FILTER (ARESAMPLE,   aresample,   af); | ||||||
|  |  | ||||||
|     REGISTER_FILTER (ANULLSRC,    anullsrc,    asrc); |     REGISTER_FILTER (ANULLSRC,    anullsrc,    asrc); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -29,8 +29,8 @@ | |||||||
| #include "libavutil/rational.h" | #include "libavutil/rational.h" | ||||||
|  |  | ||||||
| #define LIBAVFILTER_VERSION_MAJOR  2 | #define LIBAVFILTER_VERSION_MAJOR  2 | ||||||
| #define LIBAVFILTER_VERSION_MINOR 31 | #define LIBAVFILTER_VERSION_MINOR 32 | ||||||
| #define LIBAVFILTER_VERSION_MICRO  1 | #define LIBAVFILTER_VERSION_MICRO  0 | ||||||
|  |  | ||||||
| #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ | #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ | ||||||
|                                                LIBAVFILTER_VERSION_MINOR, \ |                                                LIBAVFILTER_VERSION_MINOR, \ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user