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. | ||||
|  | ||||
| @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 | ||||
|  | ||||
| @chapter Audio Sources | ||||
|   | ||||
| @@ -2,6 +2,7 @@ include $(SUBDIR)../config.mak | ||||
|  | ||||
| NAME = avfilter | ||||
| FFLIBS = avutil | ||||
| FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += avcodec | ||||
| FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec | ||||
| FFLIBS-$(CONFIG_SCALE_FILTER) += swscale | ||||
| FFLIBS-$(CONFIG_MP_FILTER) += avcodec | ||||
| @@ -20,6 +21,7 @@ OBJS-$(CONFIG_AVCODEC)                       += avcodec.o | ||||
|  | ||||
| OBJS-$(CONFIG_AFORMAT_FILTER)                += af_aformat.o | ||||
| OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o | ||||
| OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.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 (ANULL,       anull,       af); | ||||
|     REGISTER_FILTER (ARESAMPLE,   aresample,   af); | ||||
|  | ||||
|     REGISTER_FILTER (ANULLSRC,    anullsrc,    asrc); | ||||
|  | ||||
|   | ||||
| @@ -29,8 +29,8 @@ | ||||
| #include "libavutil/rational.h" | ||||
|  | ||||
| #define LIBAVFILTER_VERSION_MAJOR  2 | ||||
| #define LIBAVFILTER_VERSION_MINOR 31 | ||||
| #define LIBAVFILTER_VERSION_MICRO  1 | ||||
| #define LIBAVFILTER_VERSION_MINOR 32 | ||||
| #define LIBAVFILTER_VERSION_MICRO  0 | ||||
|  | ||||
| #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ | ||||
|                                                LIBAVFILTER_VERSION_MINOR, \ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user