From b5875b91113a0f3de6ad61e9ff8b74b81de94760 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Mon, 19 Sep 2011 06:13:30 +0200 Subject: [PATCH] Add libswresample. Similar to libswscale this does resampling and format convertion, just for audio instead of video. changing sampling rate, sample formats, channel layouts and sample packing all in one with a very simple public interface. Signed-off-by: Michael Niedermayer --- Makefile | 1 + common.mak | 2 +- configure | 7 +- ffmpeg.c | 83 ++---- libswresample/Makefile | 12 + libswresample/audioconvert.c | 113 ++++++++ libswresample/audioconvert.h | 65 +++++ libswresample/rematrix.c | 271 +++++++++++++++++ libswresample/rematrix_template.c | 38 +++ libswresample/resample2.c | 352 +++++++++++++++++++++++ libswresample/swresample.c | 432 ++++++++++++++++++++++++++++ libswresample/swresample.h | 79 +++++ libswresample/swresample_internal.h | 77 +++++ libswresample/swresample_test.c | 149 ++++++++++ tests/ref/acodec/g726 | 6 +- tests/ref/acodec/pcm | 6 +- tests/ref/lavf/dv_fmt | 4 +- tests/ref/lavf/mxf_d10 | 4 +- 18 files changed, 1634 insertions(+), 67 deletions(-) create mode 100644 libswresample/Makefile create mode 100644 libswresample/audioconvert.c create mode 100644 libswresample/audioconvert.h create mode 100644 libswresample/rematrix.c create mode 100644 libswresample/rematrix_template.c create mode 100644 libswresample/resample2.c create mode 100644 libswresample/swresample.c create mode 100644 libswresample/swresample.h create mode 100644 libswresample/swresample_internal.h create mode 100644 libswresample/swresample_test.c diff --git a/Makefile b/Makefile index 27b841f348..167dd3210e 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ FFLIBS-$(CONFIG_AVFILTER) += avfilter FFLIBS-$(CONFIG_AVFORMAT) += avformat FFLIBS-$(CONFIG_AVCODEC) += avcodec FFLIBS-$(CONFIG_POSTPROC) += postproc +FFLIBS-$(CONFIG_SWRESAMPLE)+= swresample FFLIBS-$(CONFIG_SWSCALE) += swscale FFLIBS := avutil diff --git a/common.mak b/common.mak index bad86fde3b..8dd24396d2 100644 --- a/common.mak +++ b/common.mak @@ -20,7 +20,7 @@ $(foreach VAR,$(SILENT),$(eval override $(VAR) = @$($(VAR)))) $(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_DIR)/%=%)); $(INSTALL)) endif -ALLFFLIBS = avcodec avdevice avfilter avformat avutil postproc swscale +ALLFFLIBS = avcodec avdevice avfilter avformat avutil postproc swscale swresample # NASM requires -I path terminated with / IFLAGS := -I. -I$(SRC_PATH)/ diff --git a/configure b/configure index 4cdb4916ce..dfc8ee3863 100755 --- a/configure +++ b/configure @@ -89,6 +89,7 @@ Configuration options: --disable-avdevice disable libavdevice build --disable-avcodec disable libavcodec build --disable-avformat disable libavformat build + --disable-swresample disable libswresample build --disable-swscale disable libswscale build --disable-postproc disable libpostproc build --disable-avfilter disable video filter support [no] @@ -1037,6 +1038,7 @@ CONFIG_LIST=" small sram static + swresample swscale swscale_alpha thumb @@ -1603,7 +1605,7 @@ avformat_deps="avcodec" postproc_deps="gpl" # programs -ffmpeg_deps="avcodec avformat swscale" +ffmpeg_deps="avcodec avformat swscale swresample" ffmpeg_select="buffer_filter buffersink_filter" avconv_deps="avcodec avformat swscale" avconv_select="buffer_filter" @@ -1766,6 +1768,7 @@ enable postproc enable protocols enable static enable stripping +enable swresample enable swscale enable swscale_alpha @@ -3143,7 +3146,7 @@ enabled extra_warnings && check_cflags -Winline # add some linker flags check_ldflags -Wl,--warn-common -check_ldflags -Wl,-rpath-link=libpostproc:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil +check_ldflags -Wl,-rpath-link=libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil test_ldflags -Wl,-Bsymbolic && append SHFLAGS -Wl,-Bsymbolic echo "X{};" > $TMPV diff --git a/ffmpeg.c b/ffmpeg.c index b887abd7f5..6da97d24ae 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -45,6 +45,7 @@ #include "libavutil/avstring.h" #include "libavutil/libm.h" #include "libavformat/os_support.h" +#include "libswresample/swresample.h" #include "libavformat/ffm.h" // not public API @@ -229,15 +230,14 @@ typedef struct OutputStream { /* audio only */ int audio_resample; - ReSampleContext *resample; /* for audio resampling */ int resample_sample_fmt; int resample_channels; int resample_sample_rate; - int reformat_pair; - AVAudioConvert *reformat_ctx; AVFifoBuffer *fifo; /* for compression: one audio fifo per codec */ FILE *logfile; + struct SwrContext *swr; + #if CONFIG_AVFILTER AVFilterContext *output_video_filter; AVFilterContext *input_video_filter; @@ -843,14 +843,15 @@ need_realloc: exit_program(1); } - if (enc->channels != dec->channels) + if (enc->channels != dec->channels + || enc->sample_fmt != dec->sample_fmt) ost->audio_resample = 1; resample_changed = ost->resample_sample_fmt != dec->sample_fmt || ost->resample_channels != dec->channels || ost->resample_sample_rate != dec->sample_rate; - if ((ost->audio_resample && !ost->resample) || resample_changed) { + if ((ost->audio_resample && !ost->swr) || resample_changed) { if (resample_changed) { av_log(NULL, AV_LOG_INFO, "Input stream #%d.%d frame changed from rate:%d fmt:%s ch:%d to rate:%d fmt:%s ch:%d\n", ist->file_index, ist->st->index, @@ -859,24 +860,29 @@ need_realloc: ost->resample_sample_fmt = dec->sample_fmt; ost->resample_channels = dec->channels; ost->resample_sample_rate = dec->sample_rate; - if (ost->resample) - audio_resample_close(ost->resample); + swr_free(&ost->swr); } /* if audio_sync_method is >1 the resampler is needed for audio drift compensation */ if (audio_sync_method <= 1 && ost->resample_sample_fmt == enc->sample_fmt && ost->resample_channels == enc->channels && ost->resample_sample_rate == enc->sample_rate) { - ost->resample = NULL; + //ost->swr = NULL; ost->audio_resample = 0; } else { - if (dec->sample_fmt != AV_SAMPLE_FMT_S16) - fprintf(stderr, "Warning, using s16 intermediate sample format for resampling\n"); - ost->resample = av_audio_resample_init(enc->channels, dec->channels, - enc->sample_rate, dec->sample_rate, - enc->sample_fmt, dec->sample_fmt, - 16, 10, 0, 0.8); - if (!ost->resample) { + ost->swr = swr_alloc2(ost->swr, + enc->channel_layout, enc->sample_fmt, enc->sample_rate, + dec->channel_layout, dec->sample_fmt, dec->sample_rate, + 0, NULL); + av_set_int(ost->swr, "ich", dec->channels); + av_set_int(ost->swr, "och", enc->channels); + if(audio_sync_method>1) av_set_int(ost->swr, "flags", SWR_FLAG_RESAMPLE); + if(ost->swr && swr_init(ost->swr) < 0){ + fprintf(stderr, "swr_init() failed\n"); + swr_free(&ost->swr); + } + + if (!ost->swr) { fprintf(stderr, "Can not resample %d channels @ %d Hz to %d channels @ %d Hz\n", dec->channels, dec->sample_rate, enc->channels, enc->sample_rate); @@ -885,21 +891,7 @@ need_realloc: } } -#define MAKE_SFMT_PAIR(a,b) ((a)+AV_SAMPLE_FMT_NB*(b)) - if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt && - MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt)!=ost->reformat_pair) { - if (ost->reformat_ctx) - av_audio_convert_free(ost->reformat_ctx); - ost->reformat_ctx = av_audio_convert_alloc(enc->sample_fmt, 1, - dec->sample_fmt, 1, NULL, 0); - if (!ost->reformat_ctx) { - fprintf(stderr, "Cannot convert %s sample format to %s sample format\n", - av_get_sample_fmt_name(dec->sample_fmt), - av_get_sample_fmt_name(enc->sample_fmt)); - exit_program(1); - } - ost->reformat_pair=MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt); - } + av_assert0(ost->audio_resample || dec->sample_fmt==enc->sample_fmt); if(audio_sync_method){ double delta = get_sync_ipts(ost) * enc->sample_rate - ost->sync_opts @@ -941,7 +933,7 @@ need_realloc: if(verbose > 2) fprintf(stderr, "compensating audio timestamp drift:%f compensation:%d in:%d\n", delta, comp, enc->sample_rate); // fprintf(stderr, "drift:%f len:%d opts:%"PRId64" ipts:%"PRId64" fifo:%d\n", delta, -1, ost->sync_opts, (int64_t)(get_sync_ipts(ost) * enc->sample_rate), av_fifo_size(ost->fifo)/(ost->st->codec->channels * 2)); - av_resample_compensate(*(struct AVResampleContext**)ost->resample, comp, enc->sample_rate); + swr_compensate(ost->swr, comp, enc->sample_rate); } } }else @@ -950,30 +942,15 @@ need_realloc: if (ost->audio_resample) { buftmp = audio_buf; - size_out = audio_resample(ost->resample, - (short *)buftmp, (short *)buf, - size / (dec->channels * isize)); + size_out = swr_convert(ost->swr, ( uint8_t*[]){buftmp}, audio_buf_size / (enc->channels * osize), + (const uint8_t*[]){buf }, size / (dec->channels * isize)); size_out = size_out * enc->channels * osize; } else { buftmp = buf; size_out = size; } - if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt) { - const void *ibuf[6]= {buftmp}; - void *obuf[6]= {audio_buf}; - int istride[6]= {isize}; - int ostride[6]= {osize}; - int len= size_out/istride[0]; - if (av_audio_convert(ost->reformat_ctx, obuf, ostride, ibuf, istride, len)<0) { - printf("av_audio_convert() failed\n"); - if (exit_on_error) - exit_program(1); - return; - } - buftmp = audio_buf; - size_out = len*osize; - } + av_assert0(ost->audio_resample || dec->sample_fmt==enc->sample_fmt); /* now encode as many frames as possible */ if (enc->frame_size > 1) { @@ -2133,7 +2110,6 @@ static int transcode_init(OutputFile *output_files, int nb_output_files, if (!ost->fifo) { return AVERROR(ENOMEM); } - ost->reformat_pair = MAKE_SFMT_PAIR(AV_SAMPLE_FMT_NONE,AV_SAMPLE_FMT_NONE); if (!codec->sample_rate) { codec->sample_rate = icodec->sample_rate; } @@ -2149,6 +2125,8 @@ static int transcode_init(OutputFile *output_files, int nb_output_files, if (av_get_channel_layout_nb_channels(codec->channel_layout) != codec->channels) codec->channel_layout = 0; ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1; + ost->audio_resample |= codec->sample_fmt != icodec->sample_fmt + || codec->channel_layout != icodec->channel_layout; icodec->request_channels = codec->channels; ist->decoding_needed = 1; ost->encoding_needed = 1; @@ -2679,10 +2657,7 @@ static int transcode(OutputFile *output_files, int nb_output_files, av_free(ost->forced_kf_pts); if (ost->video_resample) sws_freeContext(ost->img_resample_ctx); - if (ost->resample) - audio_resample_close(ost->resample); - if (ost->reformat_ctx) - av_audio_convert_free(ost->reformat_ctx); + swr_free(&ost->swr); av_dict_free(&ost->opts); } } diff --git a/libswresample/Makefile b/libswresample/Makefile new file mode 100644 index 0000000000..33b8ea0c58 --- /dev/null +++ b/libswresample/Makefile @@ -0,0 +1,12 @@ +include $(SUBDIR)../config.mak + +NAME = swresample +FFLIBS = avutil + +HEADERS = swresample.h + +OBJS = swresample.o audioconvert.o resample2.o rematrix.o + +TESTPROGS = swresample_test + +include $(SUBDIR)../subdir.mak diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c new file mode 100644 index 0000000000..a1fa3eb10c --- /dev/null +++ b/libswresample/audioconvert.c @@ -0,0 +1,113 @@ +/* + * audio conversion + * Copyright (c) 2006 Michael Niedermayer + * + * 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 + * audio conversion + * @author Michael Niedermayer + */ + +#include "libavutil/avstring.h" +#include "libavutil/avassert.h" +#include "libavutil/libm.h" +#include "libavutil/samplefmt.h" +#include "audioconvert.h" + + +struct AVAudioConvert { + int channels; + int fmt_pair; +}; + +AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int flags) +{ + AVAudioConvert *ctx; + ctx = av_malloc(sizeof(AVAudioConvert)); + if (!ctx) + return NULL; + ctx->channels = channels; + ctx->fmt_pair = out_fmt + AV_SAMPLE_FMT_NB*in_fmt; + return ctx; +} + +void swr_audio_convert_free(AVAudioConvert **ctx) +{ + av_freep(ctx); +} + +int swr_audio_convert(AVAudioConvert *ctx, AudioData *out, AudioData*in, int len) +{ + int ch; + + av_assert0(ctx->channels == out->ch_count); + + //FIXME optimize common cases + + for(ch=0; chchannels; ch++){ + const int is= (in ->planar ? 1 : in->ch_count) * in->bps; + const int os= (out->planar ? 1 :out->ch_count) *out->bps; + const uint8_t *pi= in ->ch[ch]; + uint8_t *po= out->ch[ch]; + uint8_t *end= po + os*len; + if(!po) + continue; + +#define CONV(ofmt, otype, ifmt, expr)\ +if(ctx->fmt_pair == ofmt + AV_SAMPLE_FMT_NB*ifmt){\ + do{\ + *(otype*)po = expr; pi += is; po += os;\ + }while(po < end);\ +} + +//FIXME put things below under ifdefs so we do not waste space for cases no codec will need +//FIXME rounding ? + + CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_U8 , *(const uint8_t*)pi) + else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<8) + else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<24) + else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7))) + else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7))) + else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S16, (*(const int16_t*)pi>>8) + 0x80) + else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S16, *(const int16_t*)pi) + else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S16, *(const int16_t*)pi<<16) + else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15))) + else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15))) + else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S32, (*(const int32_t*)pi>>24) + 0x80) + else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S32, *(const int32_t*)pi>>16) + else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S32, *(const int32_t*)pi) + else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1U<<31))) + else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1U<<31))) + else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_FLT, av_clip_uint8( lrintf(*(const float*)pi * (1<<7)) + 0x80)) + else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, av_clip_int16( lrintf(*(const float*)pi * (1<<15)))) + else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, av_clipl_int32(llrintf(*(const float*)pi * (1U<<31)))) + else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_FLT, *(const float*)pi) + else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_FLT, *(const float*)pi) + else CONV(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_DBL, av_clip_uint8( lrint(*(const double*)pi * (1<<7)) + 0x80)) + else CONV(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, av_clip_int16( lrint(*(const double*)pi * (1<<15)))) + else CONV(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, av_clipl_int32(llrint(*(const double*)pi * (1U<<31)))) + else CONV(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_DBL, *(const double*)pi) + else CONV(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_DBL, *(const double*)pi) + else return -1; + } + return 0; +} diff --git a/libswresample/audioconvert.h b/libswresample/audioconvert.h new file mode 100644 index 0000000000..e5fd4dfd80 --- /dev/null +++ b/libswresample/audioconvert.h @@ -0,0 +1,65 @@ +/* + * audio conversion + * Copyright (c) 2006 Michael Niedermayer + * Copyright (c) 2008 Peter Ross + * + * 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 + */ + +#ifndef SWR_AUDIOCONVERT_H +#define SWR_AUDIOCONVERT_H + +/** + * @file + * Audio format conversion routines + */ + + +#include "swresample_internal.h" +#include "libavutil/cpu.h" +#include "libavutil/audioconvert.h" + +struct AVAudioConvert; +typedef struct AVAudioConvert AVAudioConvert; + +/** + * Create an audio sample format converter context + * @param out_fmt Output sample format + * @param in_fmt Input sample format + * @param channels Number of channels + * @param flags See AV_CPU_FLAG_xx + * @return NULL on error + */ +AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int flags); + +/** + * Free audio sample format converter context. + * and set the pointer to NULL + */ +void swr_audio_convert_free(AVAudioConvert **ctx); + +/** + * Convert between audio sample formats + * @param[in] out array of output buffers for each channel. set to NULL to ignore processing of the given channel. + * @param[in] in array of input buffers for each channel + * @param len length of audio frame size (measured in samples) + */ +int swr_audio_convert(AVAudioConvert *ctx, AudioData *out, AudioData *in, int len); + +#endif /* AVCODEC_AUDIOCONVERT_H */ diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c new file mode 100644 index 0000000000..b8c5d45327 --- /dev/null +++ b/libswresample/rematrix.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "swresample_internal.h" +#include "libavutil/audioconvert.h" +#include "libavutil/avassert.h" + +#define SAMPLE float +#define RENAME(x) x ## _float +#include "rematrix_template.c" +#undef SAMPLE +#undef RENAME + +#define SAMPLE int16_t +#define RENAME(x) x ## _s16 +#include "rematrix_template.c" + + +#define FRONT_LEFT 0 +#define FRONT_RIGHT 1 +#define FRONT_CENTER 2 +#define LOW_FREQUENCY 3 +#define BACK_LEFT 4 +#define BACK_RIGHT 5 +#define FRONT_LEFT_OF_CENTER 6 +#define FRONT_RIGHT_OF_CENTER 7 +#define BACK_CENTER 8 +#define SIDE_LEFT 9 +#define SIDE_RIGHT 10 +#define TOP_CENTER 11 +#define TOP_FRONT_LEFT 12 +#define TOP_FRONT_CENTER 13 +#define TOP_FRONT_RIGHT 14 +#define TOP_BACK_LEFT 15 +#define TOP_BACK_CENTER 16 +#define TOP_BACK_RIGHT 17 + +static int even(int64_t layout){ + if(!layout) return 1; + if(layout&(layout-1)) return 1; + return 0; +} + +static int sane_layout(int64_t layout){ + if(!(layout & AV_CH_LAYOUT_SURROUND)) // at least 1 front speaker + return 0; + if(!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT))) // no asymetric front + return 0; + if(!even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT))) // no asymetric side + return 0; + if(!even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT))) + return 0; + if(!even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER))) + return 0; + if(av_get_channel_layout_nb_channels(layout) >= SWR_CH_MAX) + return 0; + + return 1; +} + +int swr_rematrix_init(SwrContext *s){ + int i, j, in_i, out_i; + float matrix[64][64]={0}; + int64_t unaccounted= s->in_ch_layout & ~s->out_ch_layout; + float maxcoef=0; + + for(i=0; i<64; i++){ + if(s->in_ch_layout & s->out_ch_layout & (1LL<in_ch_layout)){ + av_log(s, AV_LOG_ERROR, "Input channel layout isnt supported\n"); + return AVERROR(EINVAL); + } + if(!sane_layout(s->out_ch_layout)){ + av_log(s, AV_LOG_ERROR, "Output channel layout isnt supported\n"); + return AVERROR(EINVAL); + } + +//FIXME implement dolby surround +//FIXME implement full ac3 + + + if(unaccounted & AV_CH_FRONT_CENTER){ + if((s->out_ch_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO){ + matrix[ FRONT_LEFT][FRONT_CENTER]+= sqrt(0.5); + matrix[FRONT_RIGHT][FRONT_CENTER]+= sqrt(0.5); + }else + av_assert0(0); + } + if(unaccounted & AV_CH_LAYOUT_STEREO){ + if(s->out_ch_layout & AV_CH_FRONT_CENTER){ + matrix[FRONT_CENTER][ FRONT_LEFT]+= sqrt(0.5); + matrix[FRONT_CENTER][FRONT_RIGHT]+= sqrt(0.5); + if(s->in_ch_layout & AV_CH_FRONT_CENTER) + matrix[FRONT_CENTER][ FRONT_CENTER] = s->clev*sqrt(2); + }else + av_assert0(0); + } + + if(unaccounted & AV_CH_BACK_CENTER){ + if(s->out_ch_layout & AV_CH_BACK_LEFT){ + matrix[ BACK_LEFT][BACK_CENTER]+= sqrt(0.5); + matrix[BACK_RIGHT][BACK_CENTER]+= sqrt(0.5); + }else if(s->out_ch_layout & AV_CH_SIDE_LEFT){ + matrix[ SIDE_LEFT][BACK_CENTER]+= sqrt(0.5); + matrix[SIDE_RIGHT][BACK_CENTER]+= sqrt(0.5); + }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){ + matrix[ FRONT_LEFT][BACK_CENTER]+= s->slev*sqrt(0.5); + matrix[FRONT_RIGHT][BACK_CENTER]+= s->slev*sqrt(0.5); + }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ + matrix[ FRONT_CENTER][BACK_CENTER]+= s->slev*sqrt(0.5); + }else + av_assert0(0); + } + if(unaccounted & AV_CH_BACK_LEFT){ + if(s->out_ch_layout & AV_CH_BACK_CENTER){ + matrix[BACK_CENTER][ BACK_LEFT]+= sqrt(0.5); + matrix[BACK_CENTER][BACK_RIGHT]+= sqrt(0.5); + }else if(s->out_ch_layout & AV_CH_SIDE_LEFT){ + if(s->in_ch_layout & AV_CH_SIDE_LEFT){ + matrix[ SIDE_LEFT][ BACK_LEFT]+= sqrt(0.5); + matrix[SIDE_RIGHT][BACK_RIGHT]+= sqrt(0.5); + }else{ + matrix[ SIDE_LEFT][ BACK_LEFT]+= 1.0; + matrix[SIDE_RIGHT][BACK_RIGHT]+= 1.0; + } + }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){ + matrix[ FRONT_LEFT][ BACK_LEFT]+= s->slev; + matrix[FRONT_RIGHT][BACK_RIGHT]+= s->slev; + }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ + matrix[ FRONT_CENTER][BACK_LEFT ]+= s->slev*sqrt(0.5); + matrix[ FRONT_CENTER][BACK_RIGHT]+= s->slev*sqrt(0.5); + }else + av_assert0(0); + } + + if(unaccounted & AV_CH_SIDE_LEFT){ + if(s->out_ch_layout & AV_CH_BACK_LEFT){ + matrix[ BACK_LEFT][ SIDE_LEFT]+= 1.0; + matrix[BACK_RIGHT][SIDE_RIGHT]+= 1.0; + }else if(s->out_ch_layout & AV_CH_BACK_CENTER){ + matrix[BACK_CENTER][ SIDE_LEFT]+= sqrt(0.5); + matrix[BACK_CENTER][SIDE_RIGHT]+= sqrt(0.5); + }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){ + matrix[ FRONT_LEFT][ SIDE_LEFT]+= s->slev; + matrix[FRONT_RIGHT][SIDE_RIGHT]+= s->slev; + }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ + matrix[ FRONT_CENTER][SIDE_LEFT ]+= s->slev*sqrt(0.5); + matrix[ FRONT_CENTER][SIDE_RIGHT]+= s->slev*sqrt(0.5); + }else + av_assert0(0); + } + + if(unaccounted & AV_CH_FRONT_LEFT_OF_CENTER){ + if(s->out_ch_layout & AV_CH_FRONT_LEFT){ + matrix[ FRONT_LEFT][ FRONT_LEFT_OF_CENTER]+= 1.0; + matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER]+= 1.0; + }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){ + matrix[ FRONT_CENTER][ FRONT_LEFT_OF_CENTER]+= sqrt(0.5); + matrix[ FRONT_CENTER][FRONT_RIGHT_OF_CENTER]+= sqrt(0.5); + }else + av_assert0(0); + } + + //FIXME quantize for integeres + for(out_i=i=0; i<64; i++){ + double sum=0; + int in_i=0; + int ch_in=0; + for(j=0; j<64; j++){ + s->matrix[out_i][in_i]= matrix[i][j]; + if(matrix[i][j]){ + s->matrix_ch[out_i][++ch_in]= in_i; + sum += fabs(matrix[i][j]); + } + if(s->in_ch_layout & (1ULL<matrix_ch[out_i][0]= ch_in; + maxcoef= FFMAX(maxcoef, sum); + if(s->out_ch_layout & (1ULL<out_sample_fmt < AV_SAMPLE_FMT_FLT + || s->int_sample_fmt < AV_SAMPLE_FMT_FLT) && maxcoef > 1.0){ + for(i=0; imatrix[i][j] /= maxcoef; + } + for(i=0; iout_ch_layout); i++){ + for(j=0; jin_ch_layout); j++){ + av_log(NULL, AV_LOG_ERROR, "%f ", s->matrix[i][j]); + } + av_log(NULL, AV_LOG_ERROR, "\n"); + } + return 0; +} + +int swr_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy){ + int out_i, in_i, i, j; + +av_assert0(out->ch_count == av_get_channel_layout_nb_channels(s->out_ch_layout)); +av_assert0(in ->ch_count == av_get_channel_layout_nb_channels(s-> in_ch_layout)); + + for(out_i=0; out_ich_count; out_i++){ + switch(s->matrix_ch[out_i][0]){ + case 1: + in_i= s->matrix_ch[out_i][1]; + if(mustcopy || s->matrix[out_i][in_i]!=1.0){ + if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){ + copy_float(out->ch[out_i], in->ch[in_i], s->matrix[out_i][in_i], len); + }else + copy_s16 (out->ch[out_i], in->ch[in_i], s->matrix[out_i][in_i], len); + }else{ + out->ch[out_i]= in->ch[in_i]; + } + break; + case 2: + if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){ + sum2_float(out->ch[out_i], in->ch[ s->matrix_ch[out_i][1] ], in->ch[ s->matrix_ch[out_i][2] ], + s->matrix[out_i][ s->matrix_ch[out_i][1] ], s->matrix[out_i][ s->matrix_ch[out_i][2] ], + len); + }else{ + sum2_s16 (out->ch[out_i], in->ch[ s->matrix_ch[out_i][1] ], in->ch[ s->matrix_ch[out_i][2] ], + s->matrix[out_i][ s->matrix_ch[out_i][1] ], s->matrix[out_i][ s->matrix_ch[out_i][2] ], + len); + } + break; + default: + if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){ + for(i=0; imatrix_ch[out_i][0]; j++){ + in_i= s->matrix_ch[out_i][1+j]; + v+= ((float*)in->ch[in_i])[i] * s->matrix[out_i][in_i]; + } + ((float*)out->ch[out_i])[i]= v; + } + }else{ + for(i=0; imatrix_ch[out_i][0]; j++){ + in_i= s->matrix_ch[out_i][1+j]; + v+= ((int16_t*)in->ch[in_i])[i] * s->matrix[out_i][in_i]; //FIXME use int16 coeffs + } + ((int16_t*)out->ch[out_i])[i]= v; + } + } + } + } + return 0; +} diff --git a/libswresample/rematrix_template.c b/libswresample/rematrix_template.c new file mode 100644 index 0000000000..5d5aef2ca8 --- /dev/null +++ b/libswresample/rematrix_template.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +static void RENAME(sum2)(SAMPLE *out, const SAMPLE *in1, const SAMPLE *in2, float coeff1, float coeff2, int len){ + int i; + + for(i=0; i + * + * 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 + * audio resampling + * @author Michael Niedermayer + */ + +#include "libavutil/log.h" +#include "swresample_internal.h" + +#ifndef CONFIG_RESAMPLE_HP +#define FILTER_SHIFT 15 + +#define FELEM int16_t +#define FELEM2 int32_t +#define FELEML int64_t +#define FELEM_MAX INT16_MAX +#define FELEM_MIN INT16_MIN +#define WINDOW_TYPE 9 +#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE) +#define FILTER_SHIFT 30 + +#define FELEM int32_t +#define FELEM2 int64_t +#define FELEML int64_t +#define FELEM_MAX INT32_MAX +#define FELEM_MIN INT32_MIN +#define WINDOW_TYPE 12 +#else +#define FILTER_SHIFT 0 + +#define FELEM double +#define FELEM2 double +#define FELEML double +#define WINDOW_TYPE 24 +#endif + + +typedef struct AVResampleContext{ + const AVClass *av_class; + FELEM *filter_bank; + int filter_length; + int ideal_dst_incr; + int dst_incr; + int index; + int frac; + int src_incr; + int compensation_distance; + int phase_shift; + int phase_mask; + int linear; + double factor; +}AVResampleContext; + +/** + * 0th order modified bessel function of the first kind. + */ +static double bessel(double x){ + double v=1; + double lastv=0; + double t=1; + int i; + static const double inv[100]={ + 1.0/( 1* 1), 1.0/( 2* 2), 1.0/( 3* 3), 1.0/( 4* 4), 1.0/( 5* 5), 1.0/( 6* 6), 1.0/( 7* 7), 1.0/( 8* 8), 1.0/( 9* 9), 1.0/(10*10), + 1.0/(11*11), 1.0/(12*12), 1.0/(13*13), 1.0/(14*14), 1.0/(15*15), 1.0/(16*16), 1.0/(17*17), 1.0/(18*18), 1.0/(19*19), 1.0/(20*20), + 1.0/(21*21), 1.0/(22*22), 1.0/(23*23), 1.0/(24*24), 1.0/(25*25), 1.0/(26*26), 1.0/(27*27), 1.0/(28*28), 1.0/(29*29), 1.0/(30*30), + 1.0/(31*31), 1.0/(32*32), 1.0/(33*33), 1.0/(34*34), 1.0/(35*35), 1.0/(36*36), 1.0/(37*37), 1.0/(38*38), 1.0/(39*39), 1.0/(40*40), + 1.0/(41*41), 1.0/(42*42), 1.0/(43*43), 1.0/(44*44), 1.0/(45*45), 1.0/(46*46), 1.0/(47*47), 1.0/(48*48), 1.0/(49*49), 1.0/(50*50), + 1.0/(51*51), 1.0/(52*52), 1.0/(53*53), 1.0/(54*54), 1.0/(55*55), 1.0/(56*56), 1.0/(57*57), 1.0/(58*58), 1.0/(59*59), 1.0/(60*60), + 1.0/(61*61), 1.0/(62*62), 1.0/(63*63), 1.0/(64*64), 1.0/(65*65), 1.0/(66*66), 1.0/(67*67), 1.0/(68*68), 1.0/(69*69), 1.0/(70*70), + 1.0/(71*71), 1.0/(72*72), 1.0/(73*73), 1.0/(74*74), 1.0/(75*75), 1.0/(76*76), 1.0/(77*77), 1.0/(78*78), 1.0/(79*79), 1.0/(80*80), + 1.0/(81*81), 1.0/(82*82), 1.0/(83*83), 1.0/(84*84), 1.0/(85*85), 1.0/(86*86), 1.0/(87*87), 1.0/(88*88), 1.0/(89*89), 1.0/(90*90), + 1.0/(91*91), 1.0/(92*92), 1.0/(93*93), 1.0/(94*94), 1.0/(95*95), 1.0/(96*96), 1.0/(97*97), 1.0/(98*98), 1.0/(99*99), 1.0/(10000) + }; + + x= x*x/4; + for(i=0; v != lastv; i++){ + lastv=v; + t *= x*inv[i]; + v += t; + } + return v; +} + +/** + * builds a polyphase filterbank. + * @param factor resampling factor + * @param scale wanted sum of coefficients for each filter + * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16 + * @return 0 on success, negative on error + */ +static int build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){ + int ph, i; + double x, y, w; + double *tab = av_malloc(tap_count * sizeof(*tab)); + const int center= (tap_count-1)/2; + + if (!tab) + return AVERROR(ENOMEM); + + /* if upsampling, only need to interpolate, no filter */ + if (factor > 1.0) + factor = 1.0; + + for(ph=0;phphase_shift!=phase_shift || c->linear!=linear || c->factor != factor + || c->filter_length!=FFMAX((int)ceil(filter_size/factor), 1)){ + c= av_mallocz(sizeof(AVResampleContext)); + if (!c) + return NULL; + + c->phase_shift= phase_shift; + c->phase_mask= phase_count-1; + c->linear= linear; + c->factor= factor; + + c->filter_length= FFMAX((int)ceil(filter_size/factor), 1); + c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM)); + if (!c->filter_bank) + goto error; + if (build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM)); + c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1]; + } + + c->compensation_distance= 0; + c->src_incr= out_rate; + c->ideal_dst_incr= c->dst_incr= in_rate * phase_count; + c->index= -phase_count*((c->filter_length-1)/2); + c->frac= 0; + + return c; +error: + av_free(c->filter_bank); + av_free(c); + return NULL; +} + +void swr_resample_free(AVResampleContext **c){ + if(!*c) + return; + av_freep(&(*c)->filter_bank); + av_freep(c); +} + +void swr_compensate(struct SwrContext *s, int sample_delta, int compensation_distance){ + AVResampleContext *c= s->resample; +// sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr; + c->compensation_distance= compensation_distance; + c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance; +} + +int swr_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){ + int dst_index, i; + int index= c->index; + int frac= c->frac; + int dst_incr_frac= c->dst_incr % c->src_incr; + int dst_incr= c->dst_incr / c->src_incr; + int compensation_distance= c->compensation_distance; + + if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){ + int64_t index2= ((int64_t)index)<<32; + int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr; + dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr); + + for(dst_index=0; dst_index < dst_size; dst_index++){ + dst[dst_index] = src[index2>>32]; + index2 += incr; + } + frac += dst_index * dst_incr_frac; + index += dst_index * dst_incr; + index += frac / c->src_incr; + frac %= c->src_incr; + }else{ + for(dst_index=0; dst_index < dst_size; dst_index++){ + FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask); + int sample_index= index >> c->phase_shift; + FELEM2 val=0; + + if(sample_index < 0){ + for(i=0; ifilter_length; i++) + val += src[FFABS(sample_index + i) % src_size] * filter[i]; + }else if(sample_index + c->filter_length > src_size){ + break; + }else if(c->linear){ + FELEM2 v2=0; + for(i=0; ifilter_length; i++){ + val += src[sample_index + i] * (FELEM2)filter[i]; + v2 += src[sample_index + i] * (FELEM2)filter[i + c->filter_length]; + } + val+=(v2-val)*(FELEML)frac / c->src_incr; + }else{ + for(i=0; ifilter_length; i++){ + val += src[sample_index + i] * (FELEM2)filter[i]; + } + } + +#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE + dst[dst_index] = av_clip_int16(lrintf(val)); +#else + val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT; + dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val; +#endif + + frac += dst_incr_frac; + index += dst_incr; + if(frac >= c->src_incr){ + frac -= c->src_incr; + index++; + } + + if(dst_index + 1 == compensation_distance){ + compensation_distance= 0; + dst_incr_frac= c->ideal_dst_incr % c->src_incr; + dst_incr= c->ideal_dst_incr / c->src_incr; + } + } + } + *consumed= FFMAX(index, 0) >> c->phase_shift; + if(index>=0) index &= c->phase_mask; + + if(compensation_distance){ + compensation_distance -= dst_index; + assert(compensation_distance > 0); + } + if(update_ctx){ + c->frac= frac; + c->index= index; + c->dst_incr= dst_incr_frac + c->src_incr*dst_incr; + c->compensation_distance= compensation_distance; + } +#if 0 + if(update_ctx && !c->compensation_distance){ +#undef rand + av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2); +av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance); + } +#endif + + return dst_index; +} + +int swr_multiple_resample(AVResampleContext *c, AudioData *dst, int dst_size, AudioData *src, int src_size, int *consumed){ + int i, ret= -1; + + for(i=0; ich_count; i++){ + ret= swr_resample(c, dst->ch[i], src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + } + + return ret; +} diff --git a/libswresample/swresample.c b/libswresample/swresample.c new file mode 100644 index 0000000000..db2d1df8f8 --- /dev/null +++ b/libswresample/swresample.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/opt.h" +#include "swresample_internal.h" +#include "audioconvert.h" +#include "libavutil/avassert.h" + +#define C30DB M_SQRT2 +#define C15DB 1.189207115 +#define C__0DB 1.0 +#define C_15DB 0.840896415 +#define C_30DB M_SQRT1_2 +#define C_45DB 0.594603558 +#define C_60DB 0.5 + + +//TODO split options array out? +#define OFFSET(x) offsetof(SwrContext,x) +static const AVOption options[]={ +{"ich", "input channel count", OFFSET( in.ch_count ), FF_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0}, +{"och", "output channel count", OFFSET(out.ch_count ), FF_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0}, +{"isr", "input sample rate" , OFFSET( in_sample_rate), FF_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0}, +{"osr", "output sample rate" , OFFSET(out_sample_rate), FF_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0}, +{"ip" , "input planar" , OFFSET( in.planar ), FF_OPT_TYPE_INT, {.dbl=0}, 0, 1, 0}, +{"op" , "output planar" , OFFSET(out.planar ), FF_OPT_TYPE_INT, {.dbl=0}, 0, 1, 0}, +{"isf", "input sample format", OFFSET( in_sample_fmt ), FF_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_S16}, 0, AV_SAMPLE_FMT_NB-1, 0}, +{"osf", "output sample format", OFFSET(out_sample_fmt ), FF_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_S16}, 0, AV_SAMPLE_FMT_NB-1, 0}, +{"tsf", "internal sample format", OFFSET(int_sample_fmt ), FF_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_NONE}, -1, AV_SAMPLE_FMT_FLT, 0}, +{"icl", "input channel layout" , OFFSET( in_ch_layout), FF_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"}, +{"ocl", "output channel layout", OFFSET(out_ch_layout), FF_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"}, +{"clev", "center mix level" , OFFSET(clev) , FF_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0}, +{"slev", "sourround mix level" , OFFSET(slev) , FF_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0}, +{"flags", NULL , OFFSET(flags) , FF_OPT_TYPE_FLAGS, {.dbl=0}, 0, UINT_MAX, 0, "flags"}, +{"res", "force resampling", 0, FF_OPT_TYPE_CONST, {.dbl=SWR_FLAG_RESAMPLE}, INT_MIN, INT_MAX, 0, "flags"}, + +{0} +}; + +static const char* context_to_name(void* ptr) { + return "SWR"; +} + +static const AVClass av_class = { "SwrContext", context_to_name, options, LIBAVUTIL_VERSION_INT, OFFSET(log_level_offset), OFFSET(log_ctx) }; + +static int resample(SwrContext *s, AudioData *out_param, int out_count, + const AudioData * in_param, int in_count); + +SwrContext *swr_alloc(void){ + SwrContext *s= av_mallocz(sizeof(SwrContext)); + if(s){ + s->av_class= &av_class; + av_opt_set_defaults2(s, 0, 0); + } + return s; +} + +SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, + int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, + int log_offset, void *log_ctx){ + if(!s) s= swr_alloc(); + if(!s) return NULL; + + s->log_level_offset= log_offset; + s->log_ctx= log_ctx; + + av_set_int(s, "ocl", out_ch_layout); + av_set_int(s, "osf", out_sample_fmt); + av_set_int(s, "osr", out_sample_rate); + av_set_int(s, "icl", in_ch_layout); + av_set_int(s, "isf", in_sample_fmt); + av_set_int(s, "isr", in_sample_rate); + + s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout); + s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout); + s->int_sample_fmt = AV_SAMPLE_FMT_S16; + + return s; +} + + +static void free_temp(AudioData *a){ + av_free(a->data); + memset(a, 0, sizeof(*a)); +} + +void swr_free(SwrContext **ss){ + SwrContext *s= *ss; + if(s){ + free_temp(&s->postin); + free_temp(&s->midbuf); + free_temp(&s->preout); + free_temp(&s->in_buffer); + swr_audio_convert_free(&s-> in_convert); + swr_audio_convert_free(&s->out_convert); + swr_resample_free(&s->resample); + } + + av_freep(ss); +} + +static int64_t guess_layout(int ch){ + switch(ch){ + case 1: return AV_CH_LAYOUT_MONO; + case 2: return AV_CH_LAYOUT_STEREO; + case 5: return AV_CH_LAYOUT_5POINT0; + case 6: return AV_CH_LAYOUT_5POINT1; + case 7: return AV_CH_LAYOUT_7POINT0; + case 8: return AV_CH_LAYOUT_7POINT1; + default: return 0; + } +} + +int swr_init(SwrContext *s){ + s->in_buffer_index= 0; + s->in_buffer_count= 0; + s->resample_in_constraint= 0; + free_temp(&s->postin); + free_temp(&s->midbuf); + free_temp(&s->preout); + free_temp(&s->in_buffer); + swr_audio_convert_free(&s-> in_convert); + swr_audio_convert_free(&s->out_convert); + + //We assume AVOptions checked the various values and the defaults where allowed + if( s->int_sample_fmt != AV_SAMPLE_FMT_S16 + &&s->int_sample_fmt != AV_SAMPLE_FMT_FLT){ + av_log(s, AV_LOG_ERROR, "Requested sample format %s is not supported internally, only float & S16 is supported\n", av_get_sample_fmt_name(s->int_sample_fmt)); + return AVERROR(EINVAL); + } + + //FIXME should we allow/support using FLT on material that doesnt need it ? + if(s->in_sample_fmt <= AV_SAMPLE_FMT_S16 || s->int_sample_fmt==AV_SAMPLE_FMT_S16){ + s->int_sample_fmt= AV_SAMPLE_FMT_S16; + }else + s->int_sample_fmt= AV_SAMPLE_FMT_FLT; + + + if (s->out_sample_rate!=s->in_sample_rate || (s->flags & SWR_FLAG_RESAMPLE)){ + s->resample = swr_resample_init(s->resample, s->out_sample_rate, s->in_sample_rate, 16, 10, 0, 0.8); + }else + swr_resample_free(&s->resample); + if(s->int_sample_fmt != AV_SAMPLE_FMT_S16 && s->resample){ + av_log(s, AV_LOG_ERROR, "Resampling only supported with internal s16 currently\n"); //FIXME + return -1; + } + + if(!s-> in_ch_layout) + s-> in_ch_layout= guess_layout(s->in.ch_count); + if(!s->out_ch_layout) + s->out_ch_layout= guess_layout(s->out.ch_count); + + s->rematrix= s->out_ch_layout !=s->in_ch_layout; + +#define RSC 1 //FIXME finetune + if(!s-> in.ch_count) + s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout); + if(!s->out.ch_count) + s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout); + +av_assert0(s-> in.ch_count); +av_assert0(s->out.ch_count); + s->resample_first= RSC*s->out.ch_count/s->in.ch_count - RSC < s->out_sample_rate/(float)s-> in_sample_rate - 1.0; + + s-> in.bps= av_get_bits_per_sample_fmt(s-> in_sample_fmt)/8; + s->int_bps= av_get_bits_per_sample_fmt(s->int_sample_fmt)/8; + s->out.bps= av_get_bits_per_sample_fmt(s->out_sample_fmt)/8; + + s->in_convert = swr_audio_convert_alloc(s->int_sample_fmt, + s-> in_sample_fmt, s-> in.ch_count, 0); + s->out_convert= swr_audio_convert_alloc(s->out_sample_fmt, + s->int_sample_fmt, s->out.ch_count, 0); + + + s->postin= s->in; + s->preout= s->out; + s->midbuf= s->in; + s->in_buffer= s->in; + if(!s->resample_first){ + s->midbuf.ch_count= s->out.ch_count; + s->in_buffer.ch_count = s->out.ch_count; + } + + s->in_buffer.bps = s->postin.bps = s->midbuf.bps = s->preout.bps = s->int_bps; + s->in_buffer.planar = s->postin.planar = s->midbuf.planar = s->preout.planar = 1; + + + if(s->rematrix && swr_rematrix_init(s)<0) + return -1; + + return 0; +} + +static int realloc_audio(AudioData *a, int count){ + int i, countb; + AudioData old; + + if(a->count >= count) + return 0; + + count*=2; + + countb= FFALIGN(count*a->bps, 32); + old= *a; + + av_assert0(a->planar); + av_assert0(a->bps); + av_assert0(a->ch_count); + + a->data= av_malloc(countb*a->ch_count); + if(!a->data) + return AVERROR(ENOMEM); + for(i=0; ich_count; i++){ + a->ch[i]= a->data + i*(a->planar ? countb : a->bps); + if(a->planar) memcpy(a->ch[i], old.ch[i], a->count*a->bps); + } + av_free(old.data); + a->count= count; + + return 1; +} + +static void copy(AudioData *out, AudioData *in, + int count){ + av_assert0(out->planar == in->planar); + av_assert0(out->bps == in->bps); + av_assert0(out->ch_count == in->ch_count); + if(out->planar){ + int ch; + for(ch=0; chch_count; ch++) + memcpy(out->ch[ch], in->ch[ch], count*out->bps); + }else + memcpy(out->ch[0], in->ch[0], count*out->ch_count*out->bps); +} + +int swr_convert(struct SwrContext *s, uint8_t *out_arg[SWR_CH_MAX], int out_count, + const uint8_t *in_arg [SWR_CH_MAX], int in_count){ + AudioData *postin, *midbuf, *preout; + int ret, i/*, in_max*/; + AudioData * in= &s->in; + AudioData *out= &s->out; + AudioData preout_tmp, midbuf_tmp; + + if(!s->resample){ + if(in_count > out_count) + return -1; + out_count = in_count; + } + + av_assert0(in ->planar == 0); + av_assert0(out->planar == 0); + for(i=0; i in.ch_count; i++) + in ->ch[i]= in_arg[0] + i* in->bps; + for(i=0; iout.ch_count; i++) + out->ch[i]= out_arg[0] + i*out->bps; + +// in_max= out_count*(int64_t)s->in_sample_rate / s->out_sample_rate + resample_filter_taps; +// in_count= FFMIN(in_count, in_in + 2 - s->hist_buffer_count); + + if((ret=realloc_audio(&s->postin, in_count))<0) + return ret; + if(s->resample_first){ + av_assert0(s->midbuf.ch_count == s-> in.ch_count); + if((ret=realloc_audio(&s->midbuf, out_count))<0) + return ret; + }else{ + av_assert0(s->midbuf.ch_count == s->out.ch_count); + if((ret=realloc_audio(&s->midbuf, in_count))<0) + return ret; + } + if((ret=realloc_audio(&s->preout, out_count))<0) + return ret; + + postin= &s->postin; + + midbuf_tmp= s->midbuf; + midbuf= &midbuf_tmp; + preout_tmp= s->preout; + preout= &preout_tmp; + + if(s->int_sample_fmt == s-> in_sample_fmt && s->in.planar) + postin= in; + + if(s->resample_first ? !s->resample : !s->rematrix) + midbuf= postin; + + if(s->resample_first ? !s->rematrix : !s->resample) + preout= midbuf; + + if(s->int_sample_fmt == s->out_sample_fmt && s->out.planar){ + if(preout==in){ + out_count= FFMIN(out_count, in_count); //TODO check at teh end if this is needed or redundant + av_assert0(s->in.planar); //we only support planar internally so it has to be, we support copying non planar though + copy(out, in, out_count); + return out_count; + } + else if(preout==postin) preout= midbuf= postin= out; + else if(preout==midbuf) preout= midbuf= out; + else preout= out; + } + + if(in != postin){ + swr_audio_convert(s->in_convert, postin, in, in_count); + } + + if(s->resample_first){ + if(postin != midbuf) + out_count= resample(s, midbuf, out_count, postin, in_count); + if(midbuf != preout) + swr_rematrix(s, preout, midbuf, out_count, preout==out); + }else{ + if(postin != midbuf) + swr_rematrix(s, midbuf, postin, in_count, midbuf==out); + if(midbuf != preout) + out_count= resample(s, preout, out_count, midbuf, in_count); + } + + if(preout != out){ +//FIXME packed doesnt need more than 1 chan here! + swr_audio_convert(s->out_convert, out, preout, out_count); + } + return out_count; +} + +/** + * + * out may be equal in. + */ +static void buf_set(AudioData *out, AudioData *in, int count){ + if(in->planar){ + int ch; + for(ch=0; chch_count; ch++) + out->ch[ch]= in->ch[ch] + count*out->bps; + }else + out->ch[0]= in->ch[0] + count*out->ch_count*out->bps; +} + +/** + * + * @return number of samples output per channel + */ +static int resample(SwrContext *s, AudioData *out_param, int out_count, + const AudioData * in_param, int in_count){ + AudioData in, out, tmp; + int ret_sum=0; + int border=0; + int ch_count= s->resample_first ? s->in.ch_count : s->out.ch_count; + + tmp=out=*out_param; + in = *in_param; + + do{ + int ret, size, consumed; + if(!s->resample_in_constraint && s->in_buffer_count){ + buf_set(&tmp, &s->in_buffer, s->in_buffer_index); + ret= swr_multiple_resample(s->resample, &out, out_count, &tmp, s->in_buffer_count, &consumed); + out_count -= ret; + ret_sum += ret; + buf_set(&out, &out, ret); + s->in_buffer_count -= consumed; + s->in_buffer_index += consumed; + + if(!in_count) + break; + if(s->in_buffer_count <= border){ + buf_set(&in, &in, -s->in_buffer_count); + in_count += s->in_buffer_count; + s->in_buffer_count=0; + s->in_buffer_index=0; + border = 0; + } + } + + if(in_count && !s->in_buffer_count){ + s->in_buffer_index=0; + ret= swr_multiple_resample(s->resample, &out, out_count, &in, in_count, &consumed); + out_count -= ret; + ret_sum += ret; + buf_set(&out, &out, ret); + in_count -= consumed; + buf_set(&in, &in, consumed); + } + + //TODO is this check sane considering the advanced copy avoidance below + size= s->in_buffer_index + s->in_buffer_count + in_count; + if( size > s->in_buffer.count + && s->in_buffer_count + in_count <= s->in_buffer_index){ + buf_set(&tmp, &s->in_buffer, s->in_buffer_index); + copy(&s->in_buffer, &tmp, s->in_buffer_count); + s->in_buffer_index=0; + }else + if((ret=realloc_audio(&s->in_buffer, size)) < 0) + return ret; + + if(in_count){ + int count= in_count; + if(s->in_buffer_count && s->in_buffer_count+2 < count && out_count) count= s->in_buffer_count+2; + + buf_set(&tmp, &s->in_buffer, s->in_buffer_index + s->in_buffer_count); + copy(&tmp, &in, /*in_*/count); + s->in_buffer_count += count; + in_count -= count; + border += count; + buf_set(&in, &in, count); + s->resample_in_constraint= 0; + if(s->in_buffer_count != count || in_count) + continue; + } + break; + }while(1); + + s->resample_in_constraint= !!out_count; + + return ret_sum; +} diff --git a/libswresample/swresample.h b/libswresample/swresample.h new file mode 100644 index 0000000000..05c4f6dc01 --- /dev/null +++ b/libswresample/swresample.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWR_H +#define SWR_H + +#include +#include "libavutil/samplefmt.h" + +#define LIBSWRESAMPLE_VERSION_MAJOR 0 +#define LIBSWRESAMPLE_VERSION_MINOR 0 +#define LIBSWRESAMPLE_VERSION_MICRO 0 + +#define SWR_CH_MAX 16 + +#define SWR_FLAG_RESAMPLE 1///< Force resampling even if equal sample rate +//TODO use int resample ? +//long term TODO can we enable this dynamically? + + +struct SwrContext; + +/** + * Allocate SwrContext. + * @see swr_init(),swr_free() + * @return NULL on error + */ +struct SwrContext *swr_alloc(void); + +/** + * Initialize context after user parameters have been set. + * @return negativo n error + */ +int swr_init(struct SwrContext *s); + +/** + * Allocate SwrContext. + * @see swr_init(),swr_free() + * @return NULL on error + */ +struct SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, + int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, + int log_offset, void *log_ctx); + +/** + * Free the given SwrContext. + * And set the pointer to NULL + */ +void swr_free(struct SwrContext **s); + +/** + * Convert audio. + * @param in_count Number of input samples available in one channel. + * @param out_count Amount of space available for output in samples per channel. + * @return number of samples output per channel + */ +int swr_convert(struct SwrContext *s, uint8_t *out[SWR_CH_MAX], int out_count, + const uint8_t *in [SWR_CH_MAX], int in_count); + +void swr_compensate(struct SwrContext *s, int sample_delta, int compensation_distance); + +#endif diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h new file mode 100644 index 0000000000..17a93d8dc9 --- /dev/null +++ b/libswresample/swresample_internal.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWR_INTERNAL_H +#define SWR_INTERNAL_H + +#include "swresample.h" + +typedef struct AudioData{ + uint8_t *ch[SWR_CH_MAX]; + uint8_t *data; + int ch_count; + int bps; + int count; + int planar; +} AudioData; + +typedef struct SwrContext { //FIXME find unused fields + AVClass *av_class; + int log_level_offset; + void *log_ctx; + enum AVSampleFormat in_sample_fmt; + enum AVSampleFormat int_sample_fmt; ///%d, rate:%5d->%5d, fmt:%s->%s", + in_ch_count, out_ch_count, + in_sample_rate, out_sample_rate, + av_get_sample_fmt_name(in_sample_fmt), av_get_sample_fmt_name(out_sample_fmt)); + forw_ctx = swr_alloc2(forw_ctx, out_ch_layout, out_sample_fmt, out_sample_rate, + in_ch_layout, in_sample_fmt, in_sample_rate, 0, 0); + backw_ctx = swr_alloc2(backw_ctx,in_ch_layout, in_sample_fmt, in_sample_rate, + out_ch_layout, out_sample_fmt, out_sample_rate, 0, 0); + if(swr_init( forw_ctx) < 0) + fprintf(stderr, "swr_init(->) failed\n"); + if(swr_init(backw_ctx) < 0) + fprintf(stderr, "swr_init(<-) failed\n"); + if(!forw_ctx) + fprintf(stderr, "Failed to init forw_cts\n"); + if(!backw_ctx) + fprintf(stderr, "Failed to init backw_ctx\n"); + //FIXME test planar + for(ch=0; ch