From c5671aeb77abb18a5a10ace314ab49e8fd3d0cb3 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Tue, 24 Apr 2012 15:50:20 -0400 Subject: FATE: avoid channel mixing in lavf-dv_fmt This partially reverts acb1730218f1c614dc8ca3ba45d9de1e05059515 which would only have needed to change the checksums if channel mixing had been properly avoided. This changes the output file size reference and the seek test reference back to the previous values. --- tests/lavf-regression.sh | 2 +- tests/ref/lavf/dv_fmt | 6 +++--- tests/ref/seek/lavf_dv | 26 +++++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/lavf-regression.sh b/tests/lavf-regression.sh index 54da522b96..ab8064df16 100755 --- a/tests/lavf-regression.sh +++ b/tests/lavf-regression.sh @@ -91,7 +91,7 @@ do_lavf mov "" "-acodec pcm_alaw -c:v mpeg4" fi if [ -n "$do_dv_fmt" ] ; then -do_lavf dv "-ar 48000" "-r 25 -s pal -ac 2" +do_lavf dv "-ar 48000 -channel_layout stereo" "-r 25 -s pal" fi if [ -n "$do_gxf" ] ; then diff --git a/tests/ref/lavf/dv_fmt b/tests/ref/lavf/dv_fmt index 7615fb50ec..c2b7335001 100644 --- a/tests/ref/lavf/dv_fmt +++ b/tests/ref/lavf/dv_fmt @@ -1,3 +1,3 @@ -62577aa72e7c7fb3e781e3717a7c36cb *./tests/data/lavf/lavf.dv -3456000 ./tests/data/lavf/lavf.dv -./tests/data/lavf/lavf.dv CRC=0x37e63092 +eb51fbb48af28584ea5515f9f2400fcd *./tests/data/lavf/lavf.dv +3600000 ./tests/data/lavf/lavf.dv +./tests/data/lavf/lavf.dv CRC=0x0e868a82 diff --git a/tests/ref/seek/lavf_dv b/tests/ref/seek/lavf_dv index 8ac9404194..3c49749a6b 100644 --- a/tests/ref/seek/lavf_dv +++ b/tests/ref/seek/lavf_dv @@ -2,51 +2,51 @@ ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st:-1 flags:0 ts:-1.000000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 0 flags:0 ts: 0.800000 ret: 0 st: 0 flags:1 dts: 0.800000 pts: 0.800000 pos: -1 size:144000 ret: 0 st: 0 flags:1 ts:-0.320000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st: 1 flags:0 ts: 2.576667 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 1 flags:1 ts: 1.470833 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st:-1 flags:0 ts: 0.365002 ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos: -1 size:144000 ret: 0 st:-1 flags:1 ts:-0.740831 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st: 0 flags:0 ts: 2.160000 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 0 flags:1 ts: 1.040000 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 1 flags:0 ts:-0.058333 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st: 1 flags:1 ts: 2.835833 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st:-1 flags:0 ts: 1.730004 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st:-1 flags:1 ts: 0.624171 ret: 0 st: 0 flags:1 dts: 0.640000 pts: 0.640000 pos: -1 size:144000 ret: 0 st: 0 flags:0 ts:-0.480000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st: 0 flags:1 ts: 2.400000 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 1 flags:0 ts: 1.306667 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 1 flags:1 ts: 0.200833 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st:-1 flags:0 ts:-0.904994 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st:-1 flags:1 ts: 1.989173 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 0 flags:0 ts: 0.880000 ret: 0 st: 0 flags:1 dts: 0.880000 pts: 0.880000 pos: -1 size:144000 ret: 0 st: 0 flags:1 ts:-0.240000 ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: -1 size:144000 ret: 0 st: 1 flags:0 ts: 2.671667 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st: 1 flags:1 ts: 1.565833 -ret: 0 st: 0 flags:1 dts: 0.920000 pts: 0.920000 pos: -1 size:144000 +ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: -1 size:144000 ret: 0 st:-1 flags:0 ts: 0.460008 ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: -1 size:144000 ret: 0 st:-1 flags:1 ts:-0.645825 -- cgit v1.2.3 From c8af852b97447491823ff9b91413e32415e2babf Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Fri, 23 Mar 2012 17:42:17 -0400 Subject: Add libavresample This is a new library for audio sample format, channel layout, and sample rate conversion. --- Changelog | 1 + Makefile | 3 +- configure | 7 +- doc/APIchanges | 16 +- libavresample/Makefile | 15 ++ libavresample/audio_convert.c | 334 +++++++++++++++++++++++ libavresample/audio_convert.h | 87 ++++++ libavresample/audio_data.c | 345 ++++++++++++++++++++++++ libavresample/audio_data.h | 173 ++++++++++++ libavresample/audio_mix.c | 356 ++++++++++++++++++++++++ libavresample/audio_mix.h | 108 ++++++++ libavresample/audio_mix_matrix.c | 346 ++++++++++++++++++++++++ libavresample/avresample-test.c | 340 +++++++++++++++++++++++ libavresample/avresample.h | 283 +++++++++++++++++++ libavresample/internal.h | 75 ++++++ libavresample/libavresample.v | 4 + libavresample/options.c | 89 ++++++ libavresample/resample.c | 480 +++++++++++++++++++++++++++++++++ libavresample/resample.h | 70 +++++ libavresample/utils.c | 405 ++++++++++++++++++++++++++++ libavresample/version.h | 41 +++ libavresample/x86/Makefile | 5 + libavresample/x86/audio_convert.asm | 104 +++++++ libavresample/x86/audio_convert_init.c | 42 +++ libavresample/x86/audio_mix.asm | 64 +++++ libavresample/x86/audio_mix_init.c | 44 +++ libavutil/x86/x86util.asm | 9 + 27 files changed, 3838 insertions(+), 8 deletions(-) create mode 100644 libavresample/Makefile create mode 100644 libavresample/audio_convert.c create mode 100644 libavresample/audio_convert.h create mode 100644 libavresample/audio_data.c create mode 100644 libavresample/audio_data.h create mode 100644 libavresample/audio_mix.c create mode 100644 libavresample/audio_mix.h create mode 100644 libavresample/audio_mix_matrix.c create mode 100644 libavresample/avresample-test.c create mode 100644 libavresample/avresample.h create mode 100644 libavresample/internal.h create mode 100644 libavresample/libavresample.v create mode 100644 libavresample/options.c create mode 100644 libavresample/resample.c create mode 100644 libavresample/resample.h create mode 100644 libavresample/utils.c create mode 100644 libavresample/version.h create mode 100644 libavresample/x86/Makefile create mode 100644 libavresample/x86/audio_convert.asm create mode 100644 libavresample/x86/audio_convert_init.c create mode 100644 libavresample/x86/audio_mix.asm create mode 100644 libavresample/x86/audio_mix_init.c diff --git a/Changelog b/Changelog index 9e75dac081..99fc1dca44 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version : - RealAudio Lossless decoder - ZeroCodec decoder - drop support for avconv without libavfilter +- add libavresample audio conversion library version 0.8: diff --git a/Makefile b/Makefile index 2d9e4353a0..5da4d51b60 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ $(foreach VAR,$(SILENT),$(eval override $(VAR) = @$($(VAR)))) $(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_PATH)/%=%)); $(INSTALL)) endif -ALLFFLIBS = avcodec avdevice avfilter avformat avutil swscale +ALLFFLIBS = avcodec avdevice avfilter avformat avresample avutil swscale IFLAGS := -I. -I$(SRC_PATH) CPPFLAGS := $(IFLAGS) $(CPPFLAGS) @@ -71,6 +71,7 @@ ALLMANPAGES = $(BASENAMES:%=%.1) FFLIBS-$(CONFIG_AVDEVICE) += avdevice FFLIBS-$(CONFIG_AVFILTER) += avfilter FFLIBS-$(CONFIG_AVFORMAT) += avformat +FFLIBS-$(CONFIG_AVRESAMPLE) += avresample FFLIBS-$(CONFIG_AVCODEC) += avcodec FFLIBS-$(CONFIG_SWSCALE) += swscale diff --git a/configure b/configure index a73d666967..97db196b5e 100755 --- a/configure +++ b/configure @@ -110,6 +110,7 @@ Component options: --disable-avformat disable libavformat build --disable-swscale disable libswscale build --disable-avfilter disable video filter support [no] + --disable-avresample disable libavresample build [no] --disable-pthreads disable pthreads [auto] --disable-w32threads disable Win32 threads [auto] --enable-x11grab enable X11 grabbing [no] @@ -927,6 +928,7 @@ CONFIG_LIST=" avdevice avfilter avformat + avresample avisynth bzlib dct @@ -1536,7 +1538,7 @@ avdevice_deps="avcodec avformat" avformat_deps="avcodec" # programs -avconv_deps="avcodec avfilter avformat swscale" +avconv_deps="avcodec avfilter avformat avresample swscale" avplay_deps="avcodec avformat swscale sdl" avplay_select="rdft" avprobe_deps="avcodec avformat" @@ -1684,6 +1686,7 @@ enable avcodec enable avdevice enable avfilter enable avformat +enable avresample enable avutil enable swscale @@ -3385,6 +3388,7 @@ get_version LIBAVCODEC libavcodec/version.h get_version LIBAVDEVICE libavdevice/avdevice.h get_version LIBAVFILTER libavfilter/version.h get_version LIBAVFORMAT libavformat/version.h +get_version LIBAVRESAMPLE libavresample/version.h get_version LIBAVUTIL libavutil/avutil.h get_version LIBSWSCALE libswscale/swscale.h @@ -3504,4 +3508,5 @@ pkgconfig_generate libavcodec "Libav codec library" "$LIBAVCODEC_VERSION" "$extr pkgconfig_generate libavformat "Libav container format library" "$LIBAVFORMAT_VERSION" "$extralibs" "libavcodec = $LIBAVCODEC_VERSION" pkgconfig_generate libavdevice "Libav device handling library" "$LIBAVDEVICE_VERSION" "$extralibs" "libavformat = $LIBAVFORMAT_VERSION" pkgconfig_generate libavfilter "Libav video filtering library" "$LIBAVFILTER_VERSION" "$extralibs" +pkgconfig_generate libavresample "Libav audio resampling library" "$LIBAVRESAMPLE_VERSION" "$extralibs" pkgconfig_generate libswscale "Libav image rescaling library" "$LIBSWSCALE_VERSION" "$LIBM" "libavutil = $LIBAVUTIL_VERSION" diff --git a/doc/APIchanges b/doc/APIchanges index fed77b0eb5..ba20c80538 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,16 +2,20 @@ Never assume the API of libav* to be stable unless at least 1 month has passed since the last major version increase. The last version increases were: -libavcodec: 2012-01-27 -libavdevice: 2011-04-18 -libavfilter: 2011-04-18 -libavformat: 2012-01-27 -libswscale: 2011-06-20 -libavutil: 2011-04-18 +libavcodec: 2012-01-27 +libavdevice: 2011-04-18 +libavfilter: 2011-04-18 +libavformat: 2012-01-27 +libavresample: 2012-xx-xx +libswscale: 2011-06-20 +libavutil: 2011-04-18 API changes, most recent first: +2012-xx-xx - xxxxxxx - lavr 0.0.0 + Add libavresample audio conversion library + 2012-xx-xx - xxxxxxx - lavu 51.28.0 - audio_fifo.h Add audio FIFO functions: av_audio_fifo_free() diff --git a/libavresample/Makefile b/libavresample/Makefile new file mode 100644 index 0000000000..ce3fe81953 --- /dev/null +++ b/libavresample/Makefile @@ -0,0 +1,15 @@ +NAME = avresample +FFLIBS = avutil + +HEADERS = avresample.h \ + version.h + +OBJS = audio_convert.o \ + audio_data.o \ + audio_mix.o \ + audio_mix_matrix.o \ + options.o \ + resample.o \ + utils.o + +TESTPROGS = avresample diff --git a/libavresample/audio_convert.c b/libavresample/audio_convert.c new file mode 100644 index 0000000000..200eb100cc --- /dev/null +++ b/libavresample/audio_convert.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2006 Michael Niedermayer + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "config.h" +#include "libavutil/libm.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/samplefmt.h" +#include "audio_convert.h" +#include "audio_data.h" + +enum ConvFuncType { + CONV_FUNC_TYPE_FLAT, + CONV_FUNC_TYPE_INTERLEAVE, + CONV_FUNC_TYPE_DEINTERLEAVE, +}; + +typedef void (conv_func_flat)(uint8_t *out, const uint8_t *in, int len); + +typedef void (conv_func_interleave)(uint8_t *out, uint8_t *const *in, + int len, int channels); + +typedef void (conv_func_deinterleave)(uint8_t **out, const uint8_t *in, int len, + int channels); + +struct AudioConvert { + AVAudioResampleContext *avr; + enum AVSampleFormat in_fmt; + enum AVSampleFormat out_fmt; + int channels; + int planes; + int ptr_align; + int samples_align; + int has_optimized_func; + const char *func_descr; + const char *func_descr_generic; + enum ConvFuncType func_type; + conv_func_flat *conv_flat; + conv_func_flat *conv_flat_generic; + conv_func_interleave *conv_interleave; + conv_func_interleave *conv_interleave_generic; + conv_func_deinterleave *conv_deinterleave; + conv_func_deinterleave *conv_deinterleave_generic; +}; + +void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, int channels, + int ptr_align, int samples_align, + const char *descr, void *conv) +{ + int found = 0; + + switch (ac->func_type) { + case CONV_FUNC_TYPE_FLAT: + if (av_get_packed_sample_fmt(ac->in_fmt) == in_fmt && + av_get_packed_sample_fmt(ac->out_fmt) == out_fmt) { + ac->conv_flat = conv; + ac->func_descr = descr; + ac->ptr_align = ptr_align; + ac->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + ac->conv_flat_generic = conv; + ac->func_descr_generic = descr; + } else { + ac->has_optimized_func = 1; + } + found = 1; + } + break; + case CONV_FUNC_TYPE_INTERLEAVE: + if (ac->in_fmt == in_fmt && ac->out_fmt == out_fmt && + (!channels || ac->channels == channels)) { + ac->conv_interleave = conv; + ac->func_descr = descr; + ac->ptr_align = ptr_align; + ac->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + ac->conv_interleave_generic = conv; + ac->func_descr_generic = descr; + } else { + ac->has_optimized_func = 1; + } + found = 1; + } + break; + case CONV_FUNC_TYPE_DEINTERLEAVE: + if (ac->in_fmt == in_fmt && ac->out_fmt == out_fmt && + (!channels || ac->channels == channels)) { + ac->conv_deinterleave = conv; + ac->func_descr = descr; + ac->ptr_align = ptr_align; + ac->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + ac->conv_deinterleave_generic = conv; + ac->func_descr_generic = descr; + } else { + ac->has_optimized_func = 1; + } + found = 1; + } + break; + } + if (found) { + av_log(ac->avr, AV_LOG_DEBUG, "audio_convert: found function: %-4s " + "to %-4s (%s)\n", av_get_sample_fmt_name(ac->in_fmt), + av_get_sample_fmt_name(ac->out_fmt), descr); + } +} + +#define CONV_FUNC_NAME(dst_fmt, src_fmt) conv_ ## src_fmt ## _to_ ## dst_fmt + +#define CONV_LOOP(otype, expr) \ + do { \ + *(otype *)po = expr; \ + pi += is; \ + po += os; \ + } while (po < end); \ + +#define CONV_FUNC_FLAT(ofmt, otype, ifmt, itype, expr) \ +static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *out, const uint8_t *in, \ + int len) \ +{ \ + int is = sizeof(itype); \ + int os = sizeof(otype); \ + const uint8_t *pi = in; \ + uint8_t *po = out; \ + uint8_t *end = out + os * len; \ + CONV_LOOP(otype, expr) \ +} + +#define CONV_FUNC_INTERLEAVE(ofmt, otype, ifmt, itype, expr) \ +static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *out, const uint8_t **in, \ + int len, int channels) \ +{ \ + int ch; \ + int out_bps = sizeof(otype); \ + int is = sizeof(itype); \ + int os = channels * out_bps; \ + for (ch = 0; ch < channels; ch++) { \ + const uint8_t *pi = in[ch]; \ + uint8_t *po = out + ch * out_bps; \ + uint8_t *end = po + os * len; \ + CONV_LOOP(otype, expr) \ + } \ +} + +#define CONV_FUNC_DEINTERLEAVE(ofmt, otype, ifmt, itype, expr) \ +static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t **out, const uint8_t *in, \ + int len, int channels) \ +{ \ + int ch; \ + int in_bps = sizeof(itype); \ + int is = channels * in_bps; \ + int os = sizeof(otype); \ + for (ch = 0; ch < channels; ch++) { \ + const uint8_t *pi = in + ch * in_bps; \ + uint8_t *po = out[ch]; \ + uint8_t *end = po + os * len; \ + CONV_LOOP(otype, expr) \ + } \ +} + +#define CONV_FUNC_GROUP(ofmt, otype, ifmt, itype, expr) \ +CONV_FUNC_FLAT( ofmt, otype, ifmt, itype, expr) \ +CONV_FUNC_INTERLEAVE( ofmt, otype, ifmt ## P, itype, expr) \ +CONV_FUNC_DEINTERLEAVE(ofmt ## P, otype, ifmt, itype, expr) + +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_U8, uint8_t, *(const uint8_t *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) << 8) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) << 24) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) * (1.0f / (1 << 7))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) * (1.0 / (1 << 7))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t, (*(const int16_t *)pi >> 8) + 0x80) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi << 16) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi * (1.0f / (1 << 15))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi * (1.0 / (1 << 15))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t, (*(const int32_t *)pi >> 24) + 0x80) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi >> 16) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi * (1.0f / (1U << 31))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi * (1.0 / (1U << 31))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8( lrintf(*(const float *)pi * (1 << 7)) + 0x80)) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16( lrintf(*(const float *)pi * (1 << 15)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *)pi * (1U << 31)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_FLT, float, *(const float *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_FLT, float, *(const float *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8( lrint(*(const double *)pi * (1 << 7)) + 0x80)) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16( lrint(*(const double *)pi * (1 << 15)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *)pi * (1U << 31)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_DBL, double, *(const double *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_DBL, double, *(const double *)pi) + +#define SET_CONV_FUNC_GROUP(ofmt, ifmt) \ +ff_audio_convert_set_func(ac, ofmt, ifmt, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt, ifmt)); \ +ff_audio_convert_set_func(ac, ofmt ## P, ifmt, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt ## P, ifmt)); \ +ff_audio_convert_set_func(ac, ofmt, ifmt ## P, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt, ifmt ## P)); + +static void set_generic_function(AudioConvert *ac) +{ + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL) +} + +AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels) +{ + AudioConvert *ac; + int in_planar, out_planar; + + ac = av_mallocz(sizeof(*ac)); + if (!ac) + return NULL; + + ac->avr = avr; + ac->out_fmt = out_fmt; + ac->in_fmt = in_fmt; + ac->channels = channels; + + in_planar = av_sample_fmt_is_planar(in_fmt); + out_planar = av_sample_fmt_is_planar(out_fmt); + + if (in_planar == out_planar) { + ac->func_type = CONV_FUNC_TYPE_FLAT; + ac->planes = in_planar ? ac->channels : 1; + } else if (in_planar) + ac->func_type = CONV_FUNC_TYPE_INTERLEAVE; + else + ac->func_type = CONV_FUNC_TYPE_DEINTERLEAVE; + + set_generic_function(ac); + + if (ARCH_X86) + ff_audio_convert_init_x86(ac); + + return ac; +} + +int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in, int len) +{ + int use_generic = 1; + + /* determine whether to use the optimized function based on pointer and + samples alignment in both the input and output */ + if (ac->has_optimized_func) { + int ptr_align = FFMIN(in->ptr_align, out->ptr_align); + int samples_align = FFMIN(in->samples_align, out->samples_align); + int aligned_len = FFALIGN(len, ac->samples_align); + if (!(ptr_align % ac->ptr_align) && samples_align >= aligned_len) { + len = aligned_len; + use_generic = 0; + } + } + av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (%s)\n", len, + av_get_sample_fmt_name(ac->in_fmt), + av_get_sample_fmt_name(ac->out_fmt), + use_generic ? ac->func_descr_generic : ac->func_descr); + + switch (ac->func_type) { + case CONV_FUNC_TYPE_FLAT: { + int p; + if (!in->is_planar) + len *= in->channels; + if (use_generic) { + for (p = 0; p < ac->planes; p++) + ac->conv_flat_generic(out->data[p], in->data[p], len); + } else { + for (p = 0; p < ac->planes; p++) + ac->conv_flat(out->data[p], in->data[p], len); + } + break; + } + case CONV_FUNC_TYPE_INTERLEAVE: + if (use_generic) + ac->conv_interleave_generic(out->data[0], in->data, len, ac->channels); + else + ac->conv_interleave(out->data[0], in->data, len, ac->channels); + break; + case CONV_FUNC_TYPE_DEINTERLEAVE: + if (use_generic) + ac->conv_deinterleave_generic(out->data, in->data[0], len, ac->channels); + else + ac->conv_deinterleave(out->data, in->data[0], len, ac->channels); + break; + } + + out->nb_samples = in->nb_samples; + return 0; +} diff --git a/libavresample/audio_convert.h b/libavresample/audio_convert.h new file mode 100644 index 0000000000..9227763628 --- /dev/null +++ b/libavresample/audio_convert.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AUDIO_CONVERT_H +#define AVRESAMPLE_AUDIO_CONVERT_H + +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "audio_data.h" + +typedef struct AudioConvert AudioConvert; + +/** + * Set conversion function if the parameters match. + * + * This compares the parameters of the conversion function to the parameters + * in the AudioConvert context. If the parameters do not match, no changes are + * made to the active functions. If the parameters do match and the alignment + * is not constrained, the function is set as the generic conversion function. + * If the parameters match and the alignment is constrained, the function is + * set as the optimized conversion function. + * + * @param ac AudioConvert context + * @param out_fmt output sample format + * @param in_fmt input sample format + * @param channels number of channels, or 0 for any number of channels + * @param ptr_align buffer pointer alignment, in bytes + * @param sample_align buffer size alignment, in samples + * @param descr function type description (e.g. "C" or "SSE") + * @param conv conversion function pointer + */ +void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, int channels, + int ptr_align, int samples_align, + const char *descr, void *conv); + +/** + * Allocate and initialize AudioConvert context for sample format conversion. + * + * @param avr AVAudioResampleContext + * @param out_fmt output sample format + * @param in_fmt input sample format + * @param channels number of channels + * @return newly-allocated AudioConvert context + */ +AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels); + +/** + * Convert audio data from one sample format to another. + * + * For each call, the alignment of the input and output AudioData buffers are + * examined to determine whether to use the generic or optimized conversion + * function (when available). + * + * @param ac AudioConvert context + * @param out output audio data + * @param in input audio data + * @param len number of samples to convert + * @return 0 on success, negative AVERROR code on failure + */ +int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in, int len); + +/* arch-specific initialization functions */ + +void ff_audio_convert_init_x86(AudioConvert *ac); + +#endif /* AVRESAMPLE_AUDIO_CONVERT_H */ diff --git a/libavresample/audio_data.c b/libavresample/audio_data.c new file mode 100644 index 0000000000..3f82c50ef0 --- /dev/null +++ b/libavresample/audio_data.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/mem.h" +#include "audio_data.h" + +static const AVClass audio_data_class = { + .class_name = "AudioData", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +/* + * Calculate alignment for data pointers. + */ +static void calc_ptr_alignment(AudioData *a) +{ + int p; + int min_align = 128; + + for (p = 0; p < a->planes; p++) { + int cur_align = 128; + while ((intptr_t)a->data[p] % cur_align) + cur_align >>= 1; + if (cur_align < min_align) + min_align = cur_align; + } + a->ptr_align = min_align; +} + +int ff_audio_data_set_channels(AudioData *a, int channels) +{ + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS || + channels > a->allocated_channels) + return AVERROR(EINVAL); + + a->channels = channels; + a->planes = a->is_planar ? channels : 1; + + calc_ptr_alignment(a); + + return 0; +} + +int ff_audio_data_init(AudioData *a, void **src, int plane_size, int channels, + int nb_samples, enum AVSampleFormat sample_fmt, + int read_only, const char *name) +{ + int p; + + memset(a, 0, sizeof(*a)); + a->class = &audio_data_class; + + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(a, AV_LOG_ERROR, "invalid channel count: %d\n", channels); + return AVERROR(EINVAL); + } + + a->sample_size = av_get_bytes_per_sample(sample_fmt); + if (!a->sample_size) { + av_log(a, AV_LOG_ERROR, "invalid sample format\n"); + return AVERROR(EINVAL); + } + a->is_planar = av_sample_fmt_is_planar(sample_fmt); + a->planes = a->is_planar ? channels : 1; + a->stride = a->sample_size * (a->is_planar ? 1 : channels); + + for (p = 0; p < (a->is_planar ? channels : 1); p++) { + if (!src[p]) { + av_log(a, AV_LOG_ERROR, "invalid NULL pointer for src[%d]\n", p); + return AVERROR(EINVAL); + } + a->data[p] = src[p]; + } + a->allocated_samples = nb_samples * !read_only; + a->nb_samples = nb_samples; + a->sample_fmt = sample_fmt; + a->channels = channels; + a->allocated_channels = channels; + a->read_only = read_only; + a->allow_realloc = 0; + a->name = name ? name : "{no name}"; + + calc_ptr_alignment(a); + a->samples_align = plane_size / a->stride; + + return 0; +} + +AudioData *ff_audio_data_alloc(int channels, int nb_samples, + enum AVSampleFormat sample_fmt, const char *name) +{ + AudioData *a; + int ret; + + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) + return NULL; + + a = av_mallocz(sizeof(*a)); + if (!a) + return NULL; + + a->sample_size = av_get_bytes_per_sample(sample_fmt); + if (!a->sample_size) { + av_free(a); + return NULL; + } + a->is_planar = av_sample_fmt_is_planar(sample_fmt); + a->planes = a->is_planar ? channels : 1; + a->stride = a->sample_size * (a->is_planar ? 1 : channels); + + a->class = &audio_data_class; + a->sample_fmt = sample_fmt; + a->channels = channels; + a->allocated_channels = channels; + a->read_only = 0; + a->allow_realloc = 1; + a->name = name ? name : "{no name}"; + + if (nb_samples > 0) { + ret = ff_audio_data_realloc(a, nb_samples); + if (ret < 0) { + av_free(a); + return NULL; + } + return a; + } else { + calc_ptr_alignment(a); + return a; + } +} + +int ff_audio_data_realloc(AudioData *a, int nb_samples) +{ + int ret, new_buf_size, plane_size, p; + + /* check if buffer is already large enough */ + if (a->allocated_samples >= nb_samples) + return 0; + + /* validate that the output is not read-only and realloc is allowed */ + if (a->read_only || !a->allow_realloc) + return AVERROR(EINVAL); + + new_buf_size = av_samples_get_buffer_size(&plane_size, + a->allocated_channels, nb_samples, + a->sample_fmt, 0); + if (new_buf_size < 0) + return new_buf_size; + + /* if there is already data in the buffer and the sample format is planar, + allocate a new buffer and copy the data, otherwise just realloc the + internal buffer and set new data pointers */ + if (a->nb_samples > 0 && a->is_planar) { + uint8_t *new_data[AVRESAMPLE_MAX_CHANNELS] = { NULL }; + + ret = av_samples_alloc(new_data, &plane_size, a->allocated_channels, + nb_samples, a->sample_fmt, 0); + if (ret < 0) + return ret; + + for (p = 0; p < a->planes; p++) + memcpy(new_data[p], a->data[p], a->nb_samples * a->stride); + + av_freep(&a->buffer); + memcpy(a->data, new_data, sizeof(new_data)); + a->buffer = a->data[0]; + } else { + av_freep(&a->buffer); + a->buffer = av_malloc(new_buf_size); + if (!a->buffer) + return AVERROR(ENOMEM); + ret = av_samples_fill_arrays(a->data, &plane_size, a->buffer, + a->allocated_channels, nb_samples, + a->sample_fmt, 0); + if (ret < 0) + return ret; + } + a->buffer_size = new_buf_size; + a->allocated_samples = nb_samples; + + calc_ptr_alignment(a); + a->samples_align = plane_size / a->stride; + + return 0; +} + +void ff_audio_data_free(AudioData **a) +{ + if (!*a) + return; + av_free((*a)->buffer); + av_freep(a); +} + +int ff_audio_data_copy(AudioData *dst, AudioData *src) +{ + int ret, p; + + /* validate input/output compatibility */ + if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels) + return AVERROR(EINVAL); + + /* if the input is empty, just empty the output */ + if (!src->nb_samples) { + dst->nb_samples = 0; + return 0; + } + + /* reallocate output if necessary */ + ret = ff_audio_data_realloc(dst, src->nb_samples); + if (ret < 0) + return ret; + + /* copy data */ + for (p = 0; p < src->planes; p++) + memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride); + dst->nb_samples = src->nb_samples; + + return 0; +} + +int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src, + int src_offset, int nb_samples) +{ + int ret, p, dst_offset2, dst_move_size; + + /* validate input/output compatibility */ + if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) { + av_log(src, AV_LOG_ERROR, "sample format mismatch\n"); + return AVERROR(EINVAL); + } + + /* validate offsets are within the buffer bounds */ + if (dst_offset < 0 || dst_offset > dst->nb_samples || + src_offset < 0 || src_offset > src->nb_samples) { + av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n", + src_offset, dst_offset); + return AVERROR(EINVAL); + } + + /* check offsets and sizes to see if we can just do nothing and return */ + if (nb_samples > src->nb_samples - src_offset) + nb_samples = src->nb_samples - src_offset; + if (nb_samples <= 0) + return 0; + + /* validate that the output is not read-only */ + if (dst->read_only) { + av_log(dst, AV_LOG_ERROR, "dst is read-only\n"); + return AVERROR(EINVAL); + } + + /* reallocate output if necessary */ + ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples); + if (ret < 0) { + av_log(dst, AV_LOG_ERROR, "error reallocating dst\n"); + return ret; + } + + dst_offset2 = dst_offset + nb_samples; + dst_move_size = dst->nb_samples - dst_offset; + + for (p = 0; p < src->planes; p++) { + if (dst_move_size > 0) { + memmove(dst->data[p] + dst_offset2 * dst->stride, + dst->data[p] + dst_offset * dst->stride, + dst_move_size * dst->stride); + } + memcpy(dst->data[p] + dst_offset * dst->stride, + src->data[p] + src_offset * src->stride, + nb_samples * src->stride); + } + dst->nb_samples += nb_samples; + + return 0; +} + +void ff_audio_data_drain(AudioData *a, int nb_samples) +{ + if (a->nb_samples <= nb_samples) { + /* drain the whole buffer */ + a->nb_samples = 0; + } else { + int p; + int move_offset = a->stride * nb_samples; + int move_size = a->stride * (a->nb_samples - nb_samples); + + for (p = 0; p < a->planes; p++) + memmove(a->data[p], a->data[p] + move_offset, move_size); + + a->nb_samples -= nb_samples; + } +} + +int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset, + int nb_samples) +{ + uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS]; + int offset_size, p; + + if (offset >= a->nb_samples) + return 0; + offset_size = offset * a->stride; + for (p = 0; p < a->planes; p++) + offset_data[p] = a->data[p] + offset_size; + + return av_audio_fifo_write(af, (void **)offset_data, nb_samples); +} + +int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples) +{ + int ret; + + if (a->read_only) + return AVERROR(EINVAL); + + ret = ff_audio_data_realloc(a, nb_samples); + if (ret < 0) + return ret; + + ret = av_audio_fifo_read(af, (void **)a->data, nb_samples); + if (ret >= 0) + a->nb_samples = ret; + return ret; +} diff --git a/libavresample/audio_data.h b/libavresample/audio_data.h new file mode 100644 index 0000000000..4609ebc284 --- /dev/null +++ b/libavresample/audio_data.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AUDIO_DATA_H +#define AVRESAMPLE_AUDIO_DATA_H + +#include + +#include "libavutil/audio_fifo.h" +#include "libavutil/log.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" + +/** + * Audio buffer used for intermediate storage between conversion phases. + */ +typedef struct AudioData { + const AVClass *class; /**< AVClass for logging */ + uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; /**< data plane pointers */ + uint8_t *buffer; /**< data buffer */ + unsigned int buffer_size; /**< allocated buffer size */ + int allocated_samples; /**< number of samples the buffer can hold */ + int nb_samples; /**< current number of samples */ + enum AVSampleFormat sample_fmt; /**< sample format */ + int channels; /**< channel count */ + int allocated_channels; /**< allocated channel count */ + int is_planar; /**< sample format is planar */ + int planes; /**< number of data planes */ + int sample_size; /**< bytes per sample */ + int stride; /**< sample byte offset within a plane */ + int read_only; /**< data is read-only */ + int allow_realloc; /**< realloc is allowed */ + int ptr_align; /**< minimum data pointer alignment */ + int samples_align; /**< allocated samples alignment */ + const char *name; /**< name for debug logging */ +} AudioData; + +int ff_audio_data_set_channels(AudioData *a, int channels); + +/** + * Initialize AudioData using a given source. + * + * This does not allocate an internal buffer. It only sets the data pointers + * and audio parameters. + * + * @param a AudioData struct + * @param src source data pointers + * @param plane_size plane size, in bytes. + * This can be 0 if unknown, but that will lead to + * optimized functions not being used in many cases, + * which could slow down some conversions. + * @param channels channel count + * @param nb_samples number of samples in the source data + * @param sample_fmt sample format + * @param read_only indicates if buffer is read only or read/write + * @param name name for debug logging (can be NULL) + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_init(AudioData *a, void **src, int plane_size, int channels, + int nb_samples, enum AVSampleFormat sample_fmt, + int read_only, const char *name); + +/** + * Allocate AudioData. + * + * This allocates an internal buffer and sets audio parameters. + * + * @param channels channel count + * @param nb_samples number of samples to allocate space for + * @param sample_fmt sample format + * @param name name for debug logging (can be NULL) + * @return newly allocated AudioData struct, or NULL on error + */ +AudioData *ff_audio_data_alloc(int channels, int nb_samples, + enum AVSampleFormat sample_fmt, + const char *name); + +/** + * Reallocate AudioData. + * + * The AudioData must have been previously allocated with ff_audio_data_alloc(). + * + * @param a AudioData struct + * @param nb_samples number of samples to allocate space for + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_realloc(AudioData *a, int nb_samples); + +/** + * Free AudioData. + * + * The AudioData must have been previously allocated with ff_audio_data_alloc(). + * + * @param a AudioData struct + */ +void ff_audio_data_free(AudioData **a); + +/** + * Copy data from one AudioData to another. + * + * @param out output AudioData + * @param in input AudioData + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_copy(AudioData *out, AudioData *in); + +/** + * Append data from one AudioData to the end of another. + * + * @param dst destination AudioData + * @param dst_offset offset, in samples, to start writing, relative to the + * start of dst + * @param src source AudioData + * @param src_offset offset, in samples, to start copying, relative to the + * start of the src + * @param nb_samples number of samples to copy + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src, + int src_offset, int nb_samples); + +/** + * Drain samples from the start of the AudioData. + * + * Remaining samples are shifted to the start of the AudioData. + * + * @param a AudioData struct + * @param nb_samples number of samples to drain + */ +void ff_audio_data_drain(AudioData *a, int nb_samples); + +/** + * Add samples in AudioData to an AVAudioFifo. + * + * @param af Audio FIFO Buffer + * @param a AudioData struct + * @param offset number of samples to skip from the start of the data + * @param nb_samples number of samples to add to the FIFO + * @return number of samples actually added to the FIFO, or + * negative AVERROR code on error + */ +int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset, + int nb_samples); + +/** + * Read samples from an AVAudioFifo to AudioData. + * + * @param af Audio FIFO Buffer + * @param a AudioData struct + * @param nb_samples number of samples to read from the FIFO + * @return number of samples actually read from the FIFO, or + * negative AVERROR code on error + */ +int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples); + +#endif /* AVRESAMPLE_AUDIO_DATA_H */ diff --git a/libavresample/audio_mix.c b/libavresample/audio_mix.c new file mode 100644 index 0000000000..34252bf68d --- /dev/null +++ b/libavresample/audio_mix.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/libm.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" +#include "audio_mix.h" + +static const char *coeff_type_names[] = { "q6", "q15", "flt" }; + +void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt, + enum AVMixCoeffType coeff_type, int in_channels, + int out_channels, int ptr_align, int samples_align, + const char *descr, void *mix_func) +{ + if (fmt == am->fmt && coeff_type == am->coeff_type && + ( in_channels == am->in_channels || in_channels == 0) && + (out_channels == am->out_channels || out_channels == 0)) { + char chan_str[16]; + am->mix = mix_func; + am->func_descr = descr; + am->ptr_align = ptr_align; + am->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + am->mix_generic = mix_func; + am->func_descr_generic = descr; + } else { + am->has_optimized_func = 1; + } + if (in_channels) { + if (out_channels) + snprintf(chan_str, sizeof(chan_str), "[%d to %d] ", + in_channels, out_channels); + else + snprintf(chan_str, sizeof(chan_str), "[%d to any] ", + in_channels); + } else if (out_channels) { + snprintf(chan_str, sizeof(chan_str), "[any to %d] ", + out_channels); + } + av_log(am->avr, AV_LOG_DEBUG, "audio_mix: found function: [fmt=%s] " + "[c=%s] %s(%s)\n", av_get_sample_fmt_name(fmt), + coeff_type_names[coeff_type], + (in_channels || out_channels) ? chan_str : "", descr); + } +} + +#define MIX_FUNC_NAME(fmt, cfmt) mix_any_ ## fmt ##_## cfmt ##_c + +#define MIX_FUNC_GENERIC(fmt, cfmt, stype, ctype, sumtype, expr) \ +static void MIX_FUNC_NAME(fmt, cfmt)(stype **samples, ctype **matrix, \ + int len, int out_ch, int in_ch) \ +{ \ + int i, in, out; \ + stype temp[AVRESAMPLE_MAX_CHANNELS]; \ + for (i = 0; i < len; i++) { \ + for (out = 0; out < out_ch; out++) { \ + sumtype sum = 0; \ + for (in = 0; in < in_ch; in++) \ + sum += samples[in][i] * matrix[out][in]; \ + temp[out] = expr; \ + } \ + for (out = 0; out < out_ch; out++) \ + samples[out][i] = temp[out]; \ + } \ +} + +MIX_FUNC_GENERIC(FLTP, FLT, float, float, float, sum) +MIX_FUNC_GENERIC(S16P, FLT, int16_t, float, float, av_clip_int16(lrintf(sum))) +MIX_FUNC_GENERIC(S16P, Q15, int16_t, int32_t, int64_t, av_clip_int16(sum >> 15)) +MIX_FUNC_GENERIC(S16P, Q6, int16_t, int16_t, int32_t, av_clip_int16(sum >> 6)) + +/* TODO: templatize the channel-specific C functions */ + +static void mix_2_to_1_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float *src0 = samples[0]; + float *src1 = samples[1]; + float *dst = src0; + float m0 = matrix[0][0]; + float m1 = matrix[0][1]; + + while (len > 4) { + *dst++ = *src0++ * m0 + *src1++ * m1; + *dst++ = *src0++ * m0 + *src1++ * m1; + *dst++ = *src0++ * m0 + *src1++ * m1; + *dst++ = *src0++ * m0 + *src1++ * m1; + len -= 4; + } + while (len > 0) { + *dst++ = *src0++ * m0 + *src1++ * m1; + len--; + } +} + +static void mix_1_to_2_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float v; + float *dst0 = samples[0]; + float *dst1 = samples[1]; + float *src = dst0; + float m0 = matrix[0][0]; + float m1 = matrix[1][0]; + + while (len > 4) { + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + len -= 4; + } + while (len > 0) { + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + len--; + } +} + +static void mix_6_to_2_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float v0, v1; + float *src0 = samples[0]; + float *src1 = samples[1]; + float *src2 = samples[2]; + float *src3 = samples[3]; + float *src4 = samples[4]; + float *src5 = samples[5]; + float *dst0 = src0; + float *dst1 = src1; + float *m0 = matrix[0]; + float *m1 = matrix[1]; + + while (len > 0) { + v0 = *src0++; + v1 = *src1++; + *dst0++ = v0 * m0[0] + + v1 * m0[1] + + *src2 * m0[2] + + *src3 * m0[3] + + *src4 * m0[4] + + *src5 * m0[5]; + *dst1++ = v0 * m1[0] + + v1 * m1[1] + + *src2++ * m1[2] + + *src3++ * m1[3] + + *src4++ * m1[4] + + *src5++ * m1[5]; + len--; + } +} + +static void mix_2_to_6_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float v0, v1; + float *dst0 = samples[0]; + float *dst1 = samples[1]; + float *dst2 = samples[2]; + float *dst3 = samples[3]; + float *dst4 = samples[4]; + float *dst5 = samples[5]; + float *src0 = dst0; + float *src1 = dst1; + + while (len > 0) { + v0 = *src0++; + v1 = *src1++; + *dst0++ = v0 * matrix[0][0] + v1 * matrix[0][1]; + *dst1++ = v0 * matrix[1][0] + v1 * matrix[1][1]; + *dst2++ = v0 * matrix[2][0] + v1 * matrix[2][1]; + *dst3++ = v0 * matrix[3][0] + v1 * matrix[3][1]; + *dst4++ = v0 * matrix[4][0] + v1 * matrix[4][1]; + *dst5++ = v0 * matrix[5][0] + v1 * matrix[5][1]; + len--; + } +} + +static int mix_function_init(AudioMix *am) +{ + /* any-to-any C versions */ + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(FLTP, FLT)); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, FLT)); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q15, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, Q15)); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q6, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, Q6)); + + /* channel-specific C versions */ + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 1, 1, "C", mix_2_to_1_fltp_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 1, 1, "C", mix_1_to_2_fltp_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 6, 2, 1, 1, "C", mix_6_to_2_fltp_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 6, 1, 1, "C", mix_2_to_6_fltp_flt_c); + + if (ARCH_X86) + ff_audio_mix_init_x86(am); + + if (!am->mix) { + av_log(am->avr, AV_LOG_ERROR, "audio_mix: NO FUNCTION FOUND: [fmt=%s] " + "[c=%s] [%d to %d]\n", av_get_sample_fmt_name(am->fmt), + coeff_type_names[am->coeff_type], am->in_channels, + am->out_channels); + return AVERROR_PATCHWELCOME; + } + return 0; +} + +int ff_audio_mix_init(AVAudioResampleContext *avr) +{ + int ret; + + /* build matrix if the user did not already set one */ + if (!avr->am->matrix) { + int i, j; + char in_layout_name[128]; + char out_layout_name[128]; + double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels * + sizeof(*matrix_dbl)); + if (!matrix_dbl) + return AVERROR(ENOMEM); + + ret = avresample_build_matrix(avr->in_channel_layout, + avr->out_channel_layout, + avr->center_mix_level, + avr->surround_mix_level, + avr->lfe_mix_level, 1, matrix_dbl, + avr->in_channels); + if (ret < 0) { + av_free(matrix_dbl); + return ret; + } + + av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name), + avr->in_channels, avr->in_channel_layout); + av_get_channel_layout_string(out_layout_name, sizeof(out_layout_name), + avr->out_channels, avr->out_channel_layout); + av_log(avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n", + in_layout_name, out_layout_name); + for (i = 0; i < avr->out_channels; i++) { + for (j = 0; j < avr->in_channels; j++) { + av_log(avr, AV_LOG_DEBUG, " %0.3f ", + matrix_dbl[i * avr->in_channels + j]); + } + av_log(avr, AV_LOG_DEBUG, "\n"); + } + + ret = avresample_set_matrix(avr, matrix_dbl, avr->in_channels); + if (ret < 0) { + av_free(matrix_dbl); + return ret; + } + av_free(matrix_dbl); + } + + avr->am->fmt = avr->internal_sample_fmt; + avr->am->coeff_type = avr->mix_coeff_type; + avr->am->in_layout = avr->in_channel_layout; + avr->am->out_layout = avr->out_channel_layout; + avr->am->in_channels = avr->in_channels; + avr->am->out_channels = avr->out_channels; + + ret = mix_function_init(avr->am); + if (ret < 0) + return ret; + + return 0; +} + +void ff_audio_mix_close(AudioMix *am) +{ + if (!am) + return; + if (am->matrix) { + av_free(am->matrix[0]); + am->matrix = NULL; + } + memset(am->matrix_q6, 0, sizeof(am->matrix_q6 )); + memset(am->matrix_q15, 0, sizeof(am->matrix_q15)); + memset(am->matrix_flt, 0, sizeof(am->matrix_flt)); +} + +int ff_audio_mix(AudioMix *am, AudioData *src) +{ + int use_generic = 1; + int len = src->nb_samples; + + /* determine whether to use the optimized function based on pointer and + samples alignment in both the input and output */ + if (am->has_optimized_func) { + int aligned_len = FFALIGN(len, am->samples_align); + if (!(src->ptr_align % am->ptr_align) && + src->samples_align >= aligned_len) { + len = aligned_len; + use_generic = 0; + } + } + av_dlog(am->avr, "audio_mix: %d samples - %d to %d channels (%s)\n", + src->nb_samples, am->in_channels, am->out_channels, + use_generic ? am->func_descr_generic : am->func_descr); + + if (use_generic) + am->mix_generic(src->data, am->matrix, len, am->out_channels, + am->in_channels); + else + am->mix(src->data, am->matrix, len, am->out_channels, am->in_channels); + + ff_audio_data_set_channels(src, am->out_channels); + + return 0; +} diff --git a/libavresample/audio_mix.h b/libavresample/audio_mix.h new file mode 100644 index 0000000000..ffa1b23842 --- /dev/null +++ b/libavresample/audio_mix.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AUDIO_MIX_H +#define AVRESAMPLE_AUDIO_MIX_H + +#include + +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "audio_data.h" + +typedef void (mix_func)(uint8_t **src, void **matrix, int len, int out_ch, + int in_ch); + +typedef struct AudioMix { + AVAudioResampleContext *avr; + enum AVSampleFormat fmt; + enum AVMixCoeffType coeff_type; + uint64_t in_layout; + uint64_t out_layout; + int in_channels; + int out_channels; + + int ptr_align; + int samples_align; + int has_optimized_func; + const char *func_descr; + const char *func_descr_generic; + mix_func *mix; + mix_func *mix_generic; + + int16_t *matrix_q6[AVRESAMPLE_MAX_CHANNELS]; + int32_t *matrix_q15[AVRESAMPLE_MAX_CHANNELS]; + float *matrix_flt[AVRESAMPLE_MAX_CHANNELS]; + void **matrix; +} AudioMix; + +/** + * Set mixing function if the parameters match. + * + * This compares the parameters of the mixing function to the parameters in the + * AudioMix context. If the parameters do not match, no changes are made to the + * active functions. If the parameters do match and the alignment is not + * constrained, the function is set as the generic mixing function. If the + * parameters match and the alignment is constrained, the function is set as + * the optimized mixing function. + * + * @param am AudioMix context + * @param fmt input/output sample format + * @param coeff_type mixing coefficient type + * @param in_channels number of input channels, or 0 for any number of channels + * @param out_channels number of output channels, or 0 for any number of channels + * @param ptr_align buffer pointer alignment, in bytes + * @param sample_align buffer size alignment, in samples + * @param descr function type description (e.g. "C" or "SSE") + * @param mix_func mixing function pointer + */ +void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt, + enum AVMixCoeffType coeff_type, int in_channels, + int out_channels, int ptr_align, int samples_align, + const char *descr, void *mix_func); + +/** + * Initialize the AudioMix context in the AVAudioResampleContext. + * + * The parameters in the AVAudioResampleContext are used to initialize the + * AudioMix context and set the mixing matrix. + * + * @param avr AVAudioResampleContext + * @return 0 on success, negative AVERROR code on failure + */ +int ff_audio_mix_init(AVAudioResampleContext *avr); + +/** + * Close an AudioMix context. + * + * This clears and frees the mixing matrix arrays. + */ +void ff_audio_mix_close(AudioMix *am); + +/** + * Apply channel mixing to audio data using the current mixing matrix. + */ +int ff_audio_mix(AudioMix *am, AudioData *src); + +/* arch-specific initialization functions */ + +void ff_audio_mix_init_x86(AudioMix *am); + +#endif /* AVRESAMPLE_AUDIO_MIX_H */ diff --git a/libavresample/audio_mix_matrix.c b/libavresample/audio_mix_matrix.c new file mode 100644 index 0000000000..96c49ef26f --- /dev/null +++ b/libavresample/audio_mix_matrix.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/libm.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" +#include "audio_mix.h" + +/* channel positions */ +#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 +#define STEREO_LEFT 29 +#define STEREO_RIGHT 30 +#define WIDE_LEFT 31 +#define WIDE_RIGHT 32 +#define SURROUND_DIRECT_LEFT 33 +#define SURROUND_DIRECT_RIGHT 34 + +static av_always_inline int even(uint64_t layout) +{ + return (!layout || (layout & (layout - 1))); +} + +static int sane_layout(uint64_t layout) +{ + /* check that there is at least 1 front speaker */ + if (!(layout & AV_CH_LAYOUT_SURROUND)) + return 0; + + /* check for left/right symmetry */ + if (!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT)) || + !even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT)) || + !even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT)) || + !even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)) || + !even(layout & (AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT)) || + !even(layout & (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT)) || + !even(layout & (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT)) || + !even(layout & (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT)) || + !even(layout & (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT))) + return 0; + + return 1; +} + +int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, + double *matrix_out, int stride) +{ + int i, j, out_i, out_j; + double matrix[64][64] = {{0}}; + int64_t unaccounted = in_layout & ~out_layout; + double maxcoef = 0; + int in_channels, out_channels; + + in_channels = av_get_channel_layout_nb_channels( in_layout); + out_channels = av_get_channel_layout_nb_channels(out_layout); + + memset(matrix_out, 0, out_channels * stride * sizeof(*matrix_out)); + + /* check if layouts are supported */ + if (!in_layout || in_channels > AVRESAMPLE_MAX_CHANNELS) + return AVERROR(EINVAL); + if (!out_layout || out_channels > AVRESAMPLE_MAX_CHANNELS) + return AVERROR(EINVAL); + + /* check if layouts are unbalanced or abnormal */ + if (!sane_layout(in_layout) || !sane_layout(out_layout)) + return AVERROR_PATCHWELCOME; + + /* route matching input/output channels */ + for (i = 0; i < 64; i++) { + if (in_layout & out_layout & (1ULL << i)) + matrix[i][i] = 1.0; + } + + /* mix front center to front left/right */ + if (unaccounted & AV_CH_FRONT_CENTER) { + if ((out_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) { + matrix[FRONT_LEFT ][FRONT_CENTER] += M_SQRT1_2; + matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix front left/right to center */ + if (unaccounted & AV_CH_LAYOUT_STEREO) { + if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][FRONT_LEFT ] += M_SQRT1_2; + matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2; + /* mix left/right/center to center */ + if (in_layout & AV_CH_FRONT_CENTER) + matrix[FRONT_CENTER][FRONT_CENTER] = center_mix_level * M_SQRT2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix back center to back, side, or front */ + if (unaccounted & AV_CH_BACK_CENTER) { + if (out_layout & AV_CH_BACK_LEFT) { + matrix[BACK_LEFT ][BACK_CENTER] += M_SQRT1_2; + matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2; + } else if (out_layout & AV_CH_SIDE_LEFT) { + matrix[SIDE_LEFT ][BACK_CENTER] += M_SQRT1_2; + matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2; + } else if (out_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix back left/right to back center, side, or front */ + if (unaccounted & AV_CH_BACK_LEFT) { + if (out_layout & AV_CH_BACK_CENTER) { + matrix[BACK_CENTER][BACK_LEFT ] += M_SQRT1_2; + matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2; + } else if (out_layout & AV_CH_SIDE_LEFT) { + /* if side channels do not exist in the input, just copy back + channels to side channels, otherwise mix back into side */ + if (in_layout & AV_CH_SIDE_LEFT) { + matrix[SIDE_LEFT ][BACK_LEFT ] += M_SQRT1_2; + matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2; + } else { + matrix[SIDE_LEFT ][BACK_LEFT ] += 1.0; + matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0; + } + } else if (out_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][BACK_LEFT ] += surround_mix_level; + matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level; + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_CENTER][BACK_RIGHT] += surround_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix side left/right into back or front */ + if (unaccounted & AV_CH_SIDE_LEFT) { + if (out_layout & AV_CH_BACK_LEFT) { + /* if back channels do not exist in the input, just copy side + channels to back channels, otherwise mix side into back */ + if (in_layout & AV_CH_BACK_LEFT) { + matrix[BACK_LEFT ][SIDE_LEFT ] += M_SQRT1_2; + matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2; + } else { + matrix[BACK_LEFT ][SIDE_LEFT ] += 1.0; + matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0; + } + } else if (out_layout & AV_CH_BACK_CENTER) { + matrix[BACK_CENTER][SIDE_LEFT ] += M_SQRT1_2; + matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2; + } else if (out_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][SIDE_LEFT ] += surround_mix_level; + matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level; + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_CENTER][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix left-of-center/right-of-center into front left/right or center */ + if (unaccounted & AV_CH_FRONT_LEFT_OF_CENTER) { + if (out_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 (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER ] += M_SQRT1_2; + matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix LFE into front left/right or center */ + if (unaccounted & AV_CH_LOW_FREQUENCY) { + if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][LOW_FREQUENCY] += lfe_mix_level; + } else if (out_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + + /* transfer internal matrix to output matrix and calculate maximum + per-channel coefficient sum */ + for (out_i = i = 0; out_i < out_channels && i < 64; i++) { + double sum = 0; + for (out_j = j = 0; out_j < in_channels && j < 64; j++) { + matrix_out[out_i * stride + out_j] = matrix[i][j]; + sum += fabs(matrix[i][j]); + if (in_layout & (1ULL << j)) + out_j++; + } + maxcoef = FFMAX(maxcoef, sum); + if (out_layout & (1ULL << i)) + out_i++; + } + + /* normalize */ + if (normalize && maxcoef > 1.0) { + for (i = 0; i < out_channels; i++) + for (j = 0; j < in_channels; j++) + matrix_out[i * stride + j] /= maxcoef; + } + + return 0; +} + +int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, + int stride) +{ + int in_channels, out_channels, i, o; + + in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + + if ( in_channels < 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels < 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); + return AVERROR(EINVAL); + } + + switch (avr->mix_coeff_type) { + case AV_MIX_COEFF_TYPE_Q6: + if (!avr->am->matrix_q6[0]) { + av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); + return AVERROR(EINVAL); + } + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + matrix[o * stride + i] = avr->am->matrix_q6[o][i] / 64.0; + break; + case AV_MIX_COEFF_TYPE_Q15: + if (!avr->am->matrix_q15[0]) { + av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); + return AVERROR(EINVAL); + } + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + matrix[o * stride + i] = avr->am->matrix_q15[o][i] / 32768.0; + break; + case AV_MIX_COEFF_TYPE_FLT: + if (!avr->am->matrix_flt[0]) { + av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); + return AVERROR(EINVAL); + } + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + matrix[o * stride + i] = avr->am->matrix_flt[o][i]; + break; + default: + av_log(avr, AV_LOG_ERROR, "Invalid mix coeff type\n"); + return AVERROR(EINVAL); + } + return 0; +} + +int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, + int stride) +{ + int in_channels, out_channels, i, o; + + in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + + if ( in_channels < 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels < 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); + return AVERROR(EINVAL); + } + + if (avr->am->matrix) + av_freep(avr->am->matrix); + +#define CONVERT_MATRIX(type, expr) \ + avr->am->matrix_## type[0] = av_mallocz(out_channels * in_channels * \ + sizeof(*avr->am->matrix_## type[0])); \ + if (!avr->am->matrix_## type[0]) \ + return AVERROR(ENOMEM); \ + for (o = 0; o < out_channels; o++) { \ + if (o > 0) \ + avr->am->matrix_## type[o] = avr->am->matrix_## type[o - 1] + \ + in_channels; \ + for (i = 0; i < in_channels; i++) { \ + double v = matrix[o * stride + i]; \ + avr->am->matrix_## type[o][i] = expr; \ + } \ + } \ + avr->am->matrix = (void **)avr->am->matrix_## type; + + switch (avr->mix_coeff_type) { + case AV_MIX_COEFF_TYPE_Q6: + CONVERT_MATRIX(q6, av_clip_int16(lrint(64.0 * v))) + break; + case AV_MIX_COEFF_TYPE_Q15: + CONVERT_MATRIX(q15, av_clipl_int32(llrint(32768.0 * v))) + break; + case AV_MIX_COEFF_TYPE_FLT: + CONVERT_MATRIX(flt, v) + break; + default: + av_log(avr, AV_LOG_ERROR, "Invalid mix coeff type\n"); + return AVERROR(EINVAL); + } + + /* TODO: detect situations where we can just swap around pointers + instead of doing matrix multiplications with 0.0 and 1.0 */ + + return 0; +} diff --git a/libavresample/avresample-test.c b/libavresample/avresample-test.c new file mode 100644 index 0000000000..ad2f16d6f6 --- /dev/null +++ b/libavresample/avresample-test.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2002 Fabrice Bellard + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "libavutil/avstring.h" +#include "libavutil/lfg.h" +#include "libavutil/libm.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" + +static double dbl_rand(AVLFG *lfg) +{ + return 2.0 * (av_lfg_get(lfg) / (double)UINT_MAX) - 1.0; +} + +#define PUT_FUNC(name, fmt, type, expr) \ +static void put_sample_ ## name(void **data, enum AVSampleFormat sample_fmt,\ + int channels, int sample, int ch, \ + double v_dbl) \ +{ \ + type v = expr; \ + type **out = (type **)data; \ + if (av_sample_fmt_is_planar(sample_fmt)) \ + out[ch][sample] = v; \ + else \ + out[0][sample * channels + ch] = v; \ +} + +PUT_FUNC(u8, AV_SAMPLE_FMT_U8, uint8_t, av_clip_uint8 ( lrint(v_dbl * (1 << 7)) + 128)) +PUT_FUNC(s16, AV_SAMPLE_FMT_S16, int16_t, av_clip_int16 ( lrint(v_dbl * (1 << 15)))) +PUT_FUNC(s32, AV_SAMPLE_FMT_S32, int32_t, av_clipl_int32(llrint(v_dbl * (1U << 31)))) +PUT_FUNC(flt, AV_SAMPLE_FMT_FLT, float, v_dbl) +PUT_FUNC(dbl, AV_SAMPLE_FMT_DBL, double, v_dbl) + +static void put_sample(void **data, enum AVSampleFormat sample_fmt, + int channels, int sample, int ch, double v_dbl) +{ + switch (av_get_packed_sample_fmt(sample_fmt)) { + case AV_SAMPLE_FMT_U8: + put_sample_u8(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_S16: + put_sample_s16(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_S32: + put_sample_s32(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_FLT: + put_sample_flt(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_DBL: + put_sample_dbl(data, sample_fmt, channels, sample, ch, v_dbl); + break; + } +} + +static void audiogen(AVLFG *rnd, void **data, enum AVSampleFormat sample_fmt, + int channels, int sample_rate, int nb_samples) +{ + int i, ch, k; + double v, f, a, ampa; + double tabf1[AVRESAMPLE_MAX_CHANNELS]; + double tabf2[AVRESAMPLE_MAX_CHANNELS]; + double taba[AVRESAMPLE_MAX_CHANNELS]; + +#define PUT_SAMPLE put_sample(data, sample_fmt, channels, k, ch, v); + + k = 0; + + /* 1 second of single freq sinus at 1000 Hz */ + a = 0; + for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { + v = sin(a) * 0.30; + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + a += M_PI * 1000.0 * 2.0 / sample_rate; + } + + /* 1 second of varing frequency between 100 and 10000 Hz */ + a = 0; + for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { + v = sin(a) * 0.30; + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + f = 100.0 + (((10000.0 - 100.0) * i) / sample_rate); + a += M_PI * f * 2.0 / sample_rate; + } + + /* 0.5 second of low amplitude white noise */ + for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) { + v = dbl_rand(rnd) * 0.30; + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + } + + /* 0.5 second of high amplitude white noise */ + for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) { + v = dbl_rand(rnd); + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + } + + /* 1 second of unrelated ramps for each channel */ + for (ch = 0; ch < channels; ch++) { + taba[ch] = 0; + tabf1[ch] = 100 + av_lfg_get(rnd) % 5000; + tabf2[ch] = 100 + av_lfg_get(rnd) % 5000; + } + for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { + for (ch = 0; ch < channels; ch++) { + v = sin(taba[ch]) * 0.30; + PUT_SAMPLE + f = tabf1[ch] + (((tabf2[ch] - tabf1[ch]) * i) / sample_rate); + taba[ch] += M_PI * f * 2.0 / sample_rate; + } + } + + /* 2 seconds of 500 Hz with varying volume */ + a = 0; + ampa = 0; + for (i = 0; i < 2 * sample_rate && k < nb_samples; i++, k++) { + for (ch = 0; ch < channels; ch++) { + double amp = (1.0 + sin(ampa)) * 0.15; + if (ch & 1) + amp = 0.30 - amp; + v = sin(a) * amp; + PUT_SAMPLE + a += M_PI * 500.0 * 2.0 / sample_rate; + ampa += M_PI * 2.0 / sample_rate; + } + } +} + +/* formats, rates, and layouts are ordered for priority in testing. + e.g. 'avresample-test 4 2 2' will test all input/output combinations of + S16/FLTP/S16P/FLT, 48000/44100, and stereo/mono */ + +static const enum AVSampleFormat formats[] = { + AV_SAMPLE_FMT_S16, + AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_S16P, + AV_SAMPLE_FMT_FLT, + AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_S32, + AV_SAMPLE_FMT_U8P, + AV_SAMPLE_FMT_U8, + AV_SAMPLE_FMT_DBLP, + AV_SAMPLE_FMT_DBL, +}; + +static const int rates[] = { + 48000, + 44100, + 16000 +}; + +static const uint64_t layouts[] = { + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_5POINT1, + AV_CH_LAYOUT_7POINT1, +}; + +int main(int argc, char **argv) +{ + AVAudioResampleContext *s; + AVLFG rnd; + int ret = 0; + uint8_t *in_buf = NULL; + uint8_t *out_buf = NULL; + unsigned int in_buf_size; + unsigned int out_buf_size; + uint8_t *in_data[AVRESAMPLE_MAX_CHANNELS] = { 0 }; + uint8_t *out_data[AVRESAMPLE_MAX_CHANNELS] = { 0 }; + int in_linesize; + int out_linesize; + uint64_t in_ch_layout; + int in_channels; + enum AVSampleFormat in_fmt; + int in_rate; + uint64_t out_ch_layout; + int out_channels; + enum AVSampleFormat out_fmt; + int out_rate; + int num_formats, num_rates, num_layouts; + int i, j, k, l, m, n; + + num_formats = 2; + num_rates = 2; + num_layouts = 2; + if (argc > 1) { + if (!av_strncasecmp(argv[1], "-h", 3)) { + av_log(NULL, AV_LOG_INFO, "Usage: avresample-test [ " + "[ []]]\n" + "Default is 2 2 2\n"); + return 0; + } + num_formats = strtol(argv[1], NULL, 0); + num_formats = av_clip(num_formats, 1, FF_ARRAY_ELEMS(formats)); + } + if (argc > 2) { + num_rates = strtol(argv[2], NULL, 0); + num_rates = av_clip(num_rates, 1, FF_ARRAY_ELEMS(rates)); + } + if (argc > 3) { + num_layouts = strtol(argv[3], NULL, 0); + num_layouts = av_clip(num_layouts, 1, FF_ARRAY_ELEMS(layouts)); + } + + av_log_set_level(AV_LOG_DEBUG); + + av_lfg_init(&rnd, 0xC0FFEE); + + in_buf_size = av_samples_get_buffer_size(&in_linesize, 8, 48000 * 6, + AV_SAMPLE_FMT_DBLP, 0); + out_buf_size = in_buf_size; + + in_buf = av_malloc(in_buf_size); + if (!in_buf) + goto end; + out_buf = av_malloc(out_buf_size); + if (!out_buf) + goto end; + + s = avresample_alloc_context(); + if (!s) { + av_log(NULL, AV_LOG_ERROR, "Error allocating AVAudioResampleContext\n"); + ret = 1; + goto end; + } + + for (i = 0; i < num_formats; i++) { + in_fmt = formats[i]; + for (k = 0; k < num_layouts; k++) { + in_ch_layout = layouts[k]; + in_channels = av_get_channel_layout_nb_channels(in_ch_layout); + for (m = 0; m < num_rates; m++) { + in_rate = rates[m]; + + ret = av_samples_fill_arrays(in_data, &in_linesize, in_buf, + in_channels, in_rate * 6, + in_fmt, 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "failed in_data fill arrays\n"); + goto end; + } + audiogen(&rnd, (void **)in_data, in_fmt, in_channels, in_rate, in_rate * 6); + + for (j = 0; j < num_formats; j++) { + out_fmt = formats[j]; + for (l = 0; l < num_layouts; l++) { + out_ch_layout = layouts[l]; + out_channels = av_get_channel_layout_nb_channels(out_ch_layout); + for (n = 0; n < num_rates; n++) { + out_rate = rates[n]; + + av_log(NULL, AV_LOG_INFO, "%s to %s, %d to %d channels, %d Hz to %d Hz\n", + av_get_sample_fmt_name(in_fmt), av_get_sample_fmt_name(out_fmt), + in_channels, out_channels, in_rate, out_rate); + + ret = av_samples_fill_arrays(out_data, &out_linesize, + out_buf, out_channels, + out_rate * 6, out_fmt, 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "failed out_data fill arrays\n"); + goto end; + } + + av_opt_set_int(s, "in_channel_layout", in_ch_layout, 0); + av_opt_set_int(s, "in_sample_fmt", in_fmt, 0); + av_opt_set_int(s, "in_sample_rate", in_rate, 0); + av_opt_set_int(s, "out_channel_layout", out_ch_layout, 0); + av_opt_set_int(s, "out_sample_fmt", out_fmt, 0); + av_opt_set_int(s, "out_sample_rate", out_rate, 0); + + av_opt_set_int(s, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); + + ret = avresample_open(s); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Error opening context\n"); + goto end; + } + + ret = avresample_convert(s, (void **)out_data, out_linesize, out_rate * 6, + (void **) in_data, in_linesize, in_rate * 6); + if (ret < 0) { + char errbuf[256]; + av_strerror(ret, errbuf, sizeof(errbuf)); + av_log(NULL, AV_LOG_ERROR, "%s\n", errbuf); + goto end; + } + av_log(NULL, AV_LOG_INFO, "Converted %d samples to %d samples\n", + in_rate * 6, ret); + if (avresample_get_delay(s) > 0) + av_log(NULL, AV_LOG_INFO, "%d delay samples not converted\n", + avresample_get_delay(s)); + if (avresample_available(s) > 0) + av_log(NULL, AV_LOG_INFO, "%d samples available for output\n", + avresample_available(s)); + av_log(NULL, AV_LOG_INFO, "\n"); + + avresample_close(s); + } + } + } + } + } + } + + ret = 0; + +end: + av_freep(&in_buf); + av_freep(&out_buf); + avresample_free(&s); + return ret; +} diff --git a/libavresample/avresample.h b/libavresample/avresample.h new file mode 100644 index 0000000000..41688ed555 --- /dev/null +++ b/libavresample/avresample.h @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AVRESAMPLE_H +#define AVRESAMPLE_AVRESAMPLE_H + +/** + * @file + * external API header + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/avutil.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "libavresample/version.h" + +#define AVRESAMPLE_MAX_CHANNELS 32 + +typedef struct AVAudioResampleContext AVAudioResampleContext; + +/** Mixing Coefficient Types */ +enum AVMixCoeffType { + AV_MIX_COEFF_TYPE_Q6, /** 16-bit 10.6 fixed-point */ + AV_MIX_COEFF_TYPE_Q15, /** 32-bit 17.15 fixed-point */ + AV_MIX_COEFF_TYPE_FLT, /** floating-point */ + AV_MIX_COEFF_TYPE_NB, /** Number of coeff types. Not part of ABI */ +}; + +/** + * Return the LIBAVRESAMPLE_VERSION_INT constant. + */ +unsigned avresample_version(void); + +/** + * Return the libavresample build-time configuration. + * @return configure string + */ +const char *avresample_configuration(void); + +/** + * Return the libavresample license. + */ +const char *avresample_license(void); + +/** + * Get the AVClass for AVAudioResampleContext. + * + * Can be used in combination with AV_OPT_SEARCH_FAKE_OBJ for examining options + * without allocating a context. + * + * @see av_opt_find(). + * + * @return AVClass for AVAudioResampleContext + */ +const AVClass *avresample_get_class(void); + +/** + * Allocate AVAudioResampleContext and set options. + * + * @return allocated audio resample context, or NULL on failure + */ +AVAudioResampleContext *avresample_alloc_context(void); + +/** + * Initialize AVAudioResampleContext. + * + * @param avr audio resample context + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_open(AVAudioResampleContext *avr); + +/** + * Close AVAudioResampleContext. + * + * This closes the context, but it does not change the parameters. The context + * can be reopened with avresample_open(). It does, however, clear the output + * FIFO and any remaining leftover samples in the resampling delay buffer. If + * there was a custom matrix being used, that is also cleared. + * + * @see avresample_convert() + * @see avresample_set_matrix() + * + * @param avr audio resample context + */ +void avresample_close(AVAudioResampleContext *avr); + +/** + * Free AVAudioResampleContext and associated AVOption values. + * + * This also calls avresample_close() before freeing. + * + * @param avr audio resample context + */ +void avresample_free(AVAudioResampleContext **avr); + +/** + * Generate a channel mixing matrix. + * + * This function is the one used internally by libavresample for building the + * default mixing matrix. It is made public just as a utility function for + * building custom matrices. + * + * @param in_layout input channel layout + * @param out_layout output channel layout + * @param center_mix_level mix level for the center channel + * @param surround_mix_level mix level for the surround channel(s) + * @param lfe_mix_level mix level for the low-frequency effects channel + * @param normalize if 1, coefficients will be normalized to prevent + * overflow. if 0, coefficients will not be + * normalized. + * @param[out] matrix mixing coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o. + * @param stride distance between adjacent input channels in the + * matrix array + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, double *matrix, + int stride); + +/** + * Get the current channel mixing matrix. + * + * @param avr audio resample context + * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of + * input channel i in output channel o. + * @param stride distance between adjacent input channels in the matrix array + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, + int stride); + +/** + * Set channel mixing matrix. + * + * Allows for setting a custom mixing matrix, overriding the default matrix + * generated internally during avresample_open(). This function can be called + * anytime on an allocated context, either before or after calling + * avresample_open(). avresample_convert() always uses the current matrix. + * Calling avresample_close() on the context will clear the current matrix. + * + * @see avresample_close() + * + * @param avr audio resample context + * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of + * input channel i in output channel o. + * @param stride distance between adjacent input channels in the matrix array + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, + int stride); + +/** + * Set compensation for resampling. + * + * This can be called anytime after avresample_open(). If resampling was not + * being done previously, the AVAudioResampleContext is closed and reopened + * with resampling enabled. In this case, any samples remaining in the output + * FIFO and the current channel mixing matrix will be restored after reopening + * the context. + * + * @param avr audio resample context + * @param sample_delta compensation delta, in samples + * @param compensation_distance compensation distance, in samples + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta, + int compensation_distance); + +/** + * Convert input samples and write them to the output FIFO. + * + * The output data can be NULL or have fewer allocated samples than required. + * In this case, any remaining samples not written to the output will be added + * to an internal FIFO buffer, to be returned at the next call to this function + * or to avresample_read(). + * + * If converting sample rate, there may be data remaining in the internal + * resampling delay buffer. avresample_get_delay() tells the number of remaining + * samples. To get this data as output, call avresample_convert() with NULL + * input. + * + * At the end of the conversion process, there may be data remaining in the + * internal FIFO buffer. avresample_available() tells the number of remaining + * samples. To get this data as output, either call avresample_convert() with + * NULL input or call avresample_read(). + * + * @see avresample_available() + * @see avresample_read() + * @see avresample_get_delay() + * + * @param avr audio resample context + * @param output output data pointers + * @param out_plane_size output plane size, in bytes. + * This can be 0 if unknown, but that will lead to + * optimized functions not being used directly on the + * output, which could slow down some conversions. + * @param out_samples maximum number of samples that the output buffer can hold + * @param input input data pointers + * @param in_plane_size input plane size, in bytes + * This can be 0 if unknown, but that will lead to + * optimized functions not being used directly on the + * input, which could slow down some conversions. + * @param in_samples number of input samples to convert + * @return number of samples written to the output buffer, + * not including converted samples added to the internal + * output FIFO + */ +int avresample_convert(AVAudioResampleContext *avr, void **output, + int out_plane_size, int out_samples, void **input, + int in_plane_size, int in_samples); + +/** + * Return the number of samples currently in the resampling delay buffer. + * + * When resampling, there may be a delay between the input and output. Any + * unconverted samples in each call are stored internally in a delay buffer. + * This function allows the user to determine the current number of samples in + * the delay buffer, which can be useful for synchronization. + * + * @see avresample_convert() + * + * @param avr audio resample context + * @return number of samples currently in the resampling delay buffer + */ +int avresample_get_delay(AVAudioResampleContext *avr); + +/** + * Return the number of available samples in the output FIFO. + * + * During conversion, if the user does not specify an output buffer or + * specifies an output buffer that is smaller than what is needed, remaining + * samples that are not written to the output are stored to an internal FIFO + * buffer. The samples in the FIFO can be read with avresample_read() or + * avresample_convert(). + * + * @see avresample_read() + * @see avresample_convert() + * + * @param avr audio resample context + * @return number of samples available for reading + */ +int avresample_available(AVAudioResampleContext *avr); + +/** + * Read samples from the output FIFO. + * + * During conversion, if the user does not specify an output buffer or + * specifies an output buffer that is smaller than what is needed, remaining + * samples that are not written to the output are stored to an internal FIFO + * buffer. This function can be used to read samples from that internal FIFO. + * + * @see avresample_available() + * @see avresample_convert() + * + * @param avr audio resample context + * @param output output data pointers + * @param nb_samples number of samples to read from the FIFO + * @return the number of samples written to output + */ +int avresample_read(AVAudioResampleContext *avr, void **output, int nb_samples); + +#endif /* AVRESAMPLE_AVRESAMPLE_H */ diff --git a/libavresample/internal.h b/libavresample/internal.h new file mode 100644 index 0000000000..49ea6a668e --- /dev/null +++ b/libavresample/internal.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_INTERNAL_H +#define AVRESAMPLE_INTERNAL_H + +#include "libavutil/audio_fifo.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "audio_convert.h" +#include "audio_data.h" +#include "audio_mix.h" +#include "resample.h" + +struct AVAudioResampleContext { + const AVClass *av_class; /**< AVClass for logging and AVOptions */ + + uint64_t in_channel_layout; /**< input channel layout */ + enum AVSampleFormat in_sample_fmt; /**< input sample format */ + int in_sample_rate; /**< input sample rate */ + uint64_t out_channel_layout; /**< output channel layout */ + enum AVSampleFormat out_sample_fmt; /**< output sample format */ + int out_sample_rate; /**< output sample rate */ + enum AVSampleFormat internal_sample_fmt; /**< internal sample format */ + enum AVMixCoeffType mix_coeff_type; /**< mixing coefficient type */ + double center_mix_level; /**< center mix level */ + double surround_mix_level; /**< surround mix level */ + double lfe_mix_level; /**< lfe mix level */ + int force_resampling; /**< force resampling */ + int filter_size; /**< length of each FIR filter in the resampling filterbank relative to the cutoff frequency */ + int phase_shift; /**< log2 of the number of entries in the resampling polyphase filterbank */ + int linear_interp; /**< if 1 then the resampling FIR filter will be linearly interpolated */ + double cutoff; /**< resampling cutoff frequency. 1.0 corresponds to half the output sample rate */ + + int in_channels; /**< number of input channels */ + int out_channels; /**< number of output channels */ + int resample_channels; /**< number of channels used for resampling */ + int downmix_needed; /**< downmixing is needed */ + int upmix_needed; /**< upmixing is needed */ + int mixing_needed; /**< either upmixing or downmixing is needed */ + int resample_needed; /**< resampling is needed */ + int in_convert_needed; /**< input sample format conversion is needed */ + int out_convert_needed; /**< output sample format conversion is needed */ + + AudioData *in_buffer; /**< buffer for converted input */ + AudioData *resample_out_buffer; /**< buffer for output from resampler */ + AudioData *out_buffer; /**< buffer for converted output */ + AVAudioFifo *out_fifo; /**< FIFO for output samples */ + + AudioConvert *ac_in; /**< input sample format conversion context */ + AudioConvert *ac_out; /**< output sample format conversion context */ + ResampleContext *resample; /**< resampling context */ + AudioMix *am; /**< channel mixing context */ +}; + +#endif /* AVRESAMPLE_INTERNAL_H */ diff --git a/libavresample/libavresample.v b/libavresample/libavresample.v new file mode 100644 index 0000000000..b8c7c7d2e5 --- /dev/null +++ b/libavresample/libavresample.v @@ -0,0 +1,4 @@ +LIBAVRESAMPLE_$MAJOR { + global: av*; + local: *; +}; diff --git a/libavresample/options.c b/libavresample/options.c new file mode 100644 index 0000000000..0be1a26117 --- /dev/null +++ b/libavresample/options.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_mix.h" + +/** + * @file + * Options definition for AVAudioResampleContext. + */ + +#define OFFSET(x) offsetof(AVAudioResampleContext, x) +#define PARAM AV_OPT_FLAG_AUDIO_PARAM + +static const AVOption options[] = { + { "in_channel_layout", "Input Channel Layout", OFFSET(in_channel_layout), AV_OPT_TYPE_INT64, { 0 }, INT64_MIN, INT64_MAX, PARAM }, + { "in_sample_fmt", "Input Sample Format", OFFSET(in_sample_fmt), AV_OPT_TYPE_INT, { AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM }, + { "in_sample_rate", "Input Sample Rate", OFFSET(in_sample_rate), AV_OPT_TYPE_INT, { 48000 }, 1, INT_MAX, PARAM }, + { "out_channel_layout", "Output Channel Layout", OFFSET(out_channel_layout), AV_OPT_TYPE_INT64, { 0 }, INT64_MIN, INT64_MAX, PARAM }, + { "out_sample_fmt", "Output Sample Format", OFFSET(out_sample_fmt), AV_OPT_TYPE_INT, { AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM }, + { "out_sample_rate", "Output Sample Rate", OFFSET(out_sample_rate), AV_OPT_TYPE_INT, { 48000 }, 1, INT_MAX, PARAM }, + { "internal_sample_fmt", "Internal Sample Format", OFFSET(internal_sample_fmt), AV_OPT_TYPE_INT, { AV_SAMPLE_FMT_FLTP }, AV_SAMPLE_FMT_NONE, AV_SAMPLE_FMT_NB-1, PARAM }, + { "mix_coeff_type", "Mixing Coefficient Type", OFFSET(mix_coeff_type), AV_OPT_TYPE_INT, { AV_MIX_COEFF_TYPE_FLT }, AV_MIX_COEFF_TYPE_Q6, AV_MIX_COEFF_TYPE_NB-1, PARAM, "mix_coeff_type" }, + { "q6", "16-bit 10.6 Fixed-Point", 0, AV_OPT_TYPE_CONST, { AV_MIX_COEFF_TYPE_Q6 }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" }, + { "q15", "32-bit 17.15 Fixed-Point", 0, AV_OPT_TYPE_CONST, { AV_MIX_COEFF_TYPE_Q15 }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" }, + { "flt", "Floating-Point", 0, AV_OPT_TYPE_CONST, { AV_MIX_COEFF_TYPE_FLT }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" }, + { "center_mix_level", "Center Mix Level", OFFSET(center_mix_level), AV_OPT_TYPE_DOUBLE, { M_SQRT1_2 }, -32.0, 32.0, PARAM }, + { "surround_mix_level", "Surround Mix Level", OFFSET(surround_mix_level), AV_OPT_TYPE_DOUBLE, { M_SQRT1_2 }, -32.0, 32.0, PARAM }, + { "lfe_mix_level", "LFE Mix Level", OFFSET(lfe_mix_level), AV_OPT_TYPE_DOUBLE, { 0.0 }, -32.0, 32.0, PARAM }, + { "force_resampling", "Force Resampling", OFFSET(force_resampling), AV_OPT_TYPE_INT, { 0 }, 0, 1, PARAM }, + { "filter_size", "Resampling Filter Size", OFFSET(filter_size), AV_OPT_TYPE_INT, { 16 }, 0, 32, /* ??? */ PARAM }, + { "phase_shift", "Resampling Phase Shift", OFFSET(phase_shift), AV_OPT_TYPE_INT, { 10 }, 0, 30, /* ??? */ PARAM }, + { "linear_interp", "Use Linear Interpolation", OFFSET(linear_interp), AV_OPT_TYPE_INT, { 0 }, 0, 1, PARAM }, + { "cutoff", "Cutoff Frequency Ratio", OFFSET(cutoff), AV_OPT_TYPE_DOUBLE, { 0.8 }, 0.0, 1.0, PARAM }, + { NULL }, +}; + +static const AVClass av_resample_context_class = { + .class_name = "AVAudioResampleContext", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVAudioResampleContext *avresample_alloc_context(void) +{ + AVAudioResampleContext *avr; + + avr = av_mallocz(sizeof(*avr)); + if (!avr) + return NULL; + + avr->av_class = &av_resample_context_class; + av_opt_set_defaults(avr); + + avr->am = av_mallocz(sizeof(*avr->am)); + if (!avr->am) { + av_free(avr); + return NULL; + } + avr->am->avr = avr; + + return avr; +} + +const AVClass *avresample_get_class(void) +{ + return &av_resample_context_class; +} diff --git a/libavresample/resample.c b/libavresample/resample.c new file mode 100644 index 0000000000..5529fafe8d --- /dev/null +++ b/libavresample/resample.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2004 Michael Niedermayer + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/libm.h" +#include "libavutil/log.h" +#include "internal.h" +#include "audio_data.h" + +#ifdef CONFIG_RESAMPLE_FLT +/* float template */ +#define FILTER_SHIFT 0 +#define FELEM float +#define FELEM2 float +#define FELEML float +#define WINDOW_TYPE 24 +#elifdef CONFIG_RESAMPLE_S32 +/* s32 template */ +#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 +/* s16 template */ +#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 +#endif + +struct ResampleContext { + AVAudioResampleContext *avr; + AudioData *buffer; + 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; +}; + +/** + * 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; + + x = x * x / 4; + for (i = 1; v != lastv; i++) { + lastv = v; + t *= x / (i * i); + v += t; + } + return v; +} + +/** + * Build a polyphase filterbank. + * + * @param[out] filter filter coefficients + * @param factor resampling factor + * @param tap_count tap count + * @param phase_count phase count + * @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 AVERROR code on failure + */ +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; + const int center = (tap_count - 1) / 2; + + tab = av_malloc(tap_count * sizeof(*tab)); + if (!tab) + return AVERROR(ENOMEM); + + /* if upsampling, only need to interpolate, no filter */ + if (factor > 1.0) + factor = 1.0; + + for (ph = 0; ph < phase_count; ph++) { + double norm = 0; + for (i = 0; i < tap_count; i++) { + x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor; + if (x == 0) y = 1.0; + else y = sin(x) / x; + switch (type) { + case 0: { + const float d = -0.5; //first order derivative = -0.5 + x = fabs(((double)(i - center) - (double)ph / phase_count) * factor); + if (x < 1.0) y = 1 - 3 * x*x + 2 * x*x*x + d * ( -x*x + x*x*x); + else y = d * (-4 + 8 * x - 5 * x*x + x*x*x); + break; + } + case 1: + w = 2.0 * x / (factor * tap_count) + M_PI; + y *= 0.3635819 - 0.4891775 * cos( w) + + 0.1365995 * cos(2 * w) - + 0.0106411 * cos(3 * w); + break; + default: + w = 2.0 * x / (factor * tap_count * M_PI); + y *= bessel(type * sqrt(FFMAX(1 - w * w, 0))); + break; + } + + tab[i] = y; + norm += y; + } + + /* normalize so that an uniform color remains the same */ + for (i = 0; i < tap_count; i++) { +#ifdef CONFIG_RESAMPLE_FLT + filter[ph * tap_count + i] = tab[i] / norm; +#else + filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), + FELEM_MIN, FELEM_MAX); +#endif + } + } + + av_free(tab); + return 0; +} + +ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr) +{ + ResampleContext *c; + int out_rate = avr->out_sample_rate; + int in_rate = avr->in_sample_rate; + double factor = FFMIN(out_rate * avr->cutoff / in_rate, 1.0); + int phase_count = 1 << avr->phase_shift; + + /* TODO: add support for s32 and float internal formats */ + if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P) { + av_log(avr, AV_LOG_ERROR, "Unsupported internal format for " + "resampling: %s\n", + av_get_sample_fmt_name(avr->internal_sample_fmt)); + return NULL; + } + c = av_mallocz(sizeof(*c)); + if (!c) + return NULL; + + c->avr = avr; + c->phase_shift = avr->phase_shift; + c->phase_mask = phase_count - 1; + c->linear = avr->linear_interp; + c->factor = factor; + c->filter_length = FFMAX((int)ceil(avr->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_SHIFT, WINDOW_TYPE) < 0) + goto error; + + memcpy(&c->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; + if (!av_reduce(&c->src_incr, &c->dst_incr, out_rate, + in_rate * (int64_t)phase_count, INT32_MAX / 2)) + goto error; + c->ideal_dst_incr = c->dst_incr; + + c->index = -phase_count * ((c->filter_length - 1) / 2); + c->frac = 0; + + /* allocate internal buffer */ + c->buffer = ff_audio_data_alloc(avr->resample_channels, 0, + avr->internal_sample_fmt, + "resample buffer"); + if (!c->buffer) + goto error; + + av_log(avr, AV_LOG_DEBUG, "resample: %s from %d Hz to %d Hz\n", + av_get_sample_fmt_name(avr->internal_sample_fmt), + avr->in_sample_rate, avr->out_sample_rate); + + return c; + +error: + ff_audio_data_free(&c->buffer); + av_free(c->filter_bank); + av_free(c); + return NULL; +} + +void ff_audio_resample_free(ResampleContext **c) +{ + if (!*c) + return; + ff_audio_data_free(&(*c)->buffer); + av_free((*c)->filter_bank); + av_freep(c); +} + +int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta, + int compensation_distance) +{ + ResampleContext *c; + AudioData *fifo_buf = NULL; + int ret = 0; + + if (compensation_distance < 0) + return AVERROR(EINVAL); + if (!compensation_distance && sample_delta) + return AVERROR(EINVAL); + + /* if resampling was not enabled previously, re-initialize the + AVAudioResampleContext and force resampling */ + if (!avr->resample_needed) { + int fifo_samples; + double matrix[AVRESAMPLE_MAX_CHANNELS * AVRESAMPLE_MAX_CHANNELS] = { 0 }; + + /* buffer any remaining samples in the output FIFO before closing */ + fifo_samples = av_audio_fifo_size(avr->out_fifo); + if (fifo_samples > 0) { + fifo_buf = ff_audio_data_alloc(avr->out_channels, fifo_samples, + avr->out_sample_fmt, NULL); + if (!fifo_buf) + return AVERROR(EINVAL); + ret = ff_audio_data_read_from_fifo(avr->out_fifo, fifo_buf, + fifo_samples); + if (ret < 0) + goto reinit_fail; + } + /* save the channel mixing matrix */ + ret = avresample_get_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS); + if (ret < 0) + goto reinit_fail; + + /* close the AVAudioResampleContext */ + avresample_close(avr); + + avr->force_resampling = 1; + + /* restore the channel mixing matrix */ + ret = avresample_set_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS); + if (ret < 0) + goto reinit_fail; + + /* re-open the AVAudioResampleContext */ + ret = avresample_open(avr); + if (ret < 0) + goto reinit_fail; + + /* restore buffered samples to the output FIFO */ + if (fifo_samples > 0) { + ret = ff_audio_data_add_to_fifo(avr->out_fifo, fifo_buf, 0, + fifo_samples); + if (ret < 0) + goto reinit_fail; + ff_audio_data_free(&fifo_buf); + } + } + c = avr->resample; + c->compensation_distance = compensation_distance; + if (compensation_distance) { + c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * + (int64_t)sample_delta / compensation_distance; + } else { + c->dst_incr = c->ideal_dst_incr; + } + return 0; + +reinit_fail: + ff_audio_data_free(&fifo_buf); + return ret; +} + +static int resample(ResampleContext *c, int16_t *dst, const int16_t *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 (!dst != !src) + return AVERROR(EINVAL); + + 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); + + if (dst) { + for(dst_index = 0; dst_index < dst_size; dst_index++) { + dst[dst_index] = src[index2 >> 32]; + index2 += incr; + } + } else { + dst_index = dst_size; + } + index += dst_index * dst_incr; + index += (frac + dst_index * (int64_t)dst_incr_frac) / c->src_incr; + frac = (frac + dst_index * (int64_t)dst_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; + + if (!dst && (sample_index + c->filter_length > src_size || + -sample_index >= src_size)) + break; + + if (dst) { + FELEM2 val = 0; + + if (sample_index < 0) { + for (i = 0; i < c->filter_length; i++) + val += src[FFABS(sample_index + i) % src_size] * + (FELEM2)filter[i]; + } else if (sample_index + c->filter_length > src_size) { + break; + } else if (c->linear) { + FELEM2 v2 = 0; + for (i = 0; i < c->filter_length; i++) { + val += src[abs(sample_index + i)] * (FELEM2)filter[i]; + v2 += src[abs(sample_index + i)] * (FELEM2)filter[i + c->filter_length]; + } + val += (v2 - val) * (FELEML)frac / c->src_incr; + } else { + for (i = 0; i < c->filter_length; i++) + val += src[sample_index + i] * (FELEM2)filter[i]; + } + +#ifdef CONFIG_RESAMPLE_FLT + dst[dst_index] = av_clip_int16(lrintf(val)); +#else + val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT; + dst[dst_index] = av_clip_int16(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; + } + } + } + if (consumed) + *consumed = FFMAX(index, 0) >> c->phase_shift; + + if (update_ctx) { + if (index >= 0) + index &= c->phase_mask; + + if (compensation_distance) { + compensation_distance -= dst_index; + if (compensation_distance <= 0) + return AVERROR_BUG; + } + c->frac = frac; + c->index = index; + c->dst_incr = dst_incr_frac + c->src_incr*dst_incr; + c->compensation_distance = compensation_distance; + } + + return dst_index; +} + +int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src, + int *consumed) +{ + int ch, in_samples, in_leftover, out_samples = 0; + int ret = AVERROR(EINVAL); + + in_samples = src ? src->nb_samples : 0; + in_leftover = c->buffer->nb_samples; + + /* add input samples to the internal buffer */ + if (src) { + ret = ff_audio_data_combine(c->buffer, in_leftover, src, 0, in_samples); + if (ret < 0) + return ret; + } else if (!in_leftover) { + /* no remaining samples to flush */ + return 0; + } else { + /* TODO: pad buffer to flush completely */ + } + + /* calculate output size and reallocate output buffer if needed */ + /* TODO: try to calculate this without the dummy resample() run */ + if (!dst->read_only && dst->allow_realloc) { + out_samples = resample(c, NULL, NULL, NULL, c->buffer->nb_samples, + INT_MAX, 0); + ret = ff_audio_data_realloc(dst, out_samples); + if (ret < 0) { + av_log(c->avr, AV_LOG_ERROR, "error reallocating output\n"); + return ret; + } + } + + /* resample each channel plane */ + for (ch = 0; ch < c->buffer->channels; ch++) { + out_samples = resample(c, (int16_t *)dst->data[ch], + (const int16_t *)c->buffer->data[ch], consumed, + c->buffer->nb_samples, dst->allocated_samples, + ch + 1 == c->buffer->channels); + } + if (out_samples < 0) { + av_log(c->avr, AV_LOG_ERROR, "error during resampling\n"); + return out_samples; + } + + /* drain consumed samples from the internal buffer */ + ff_audio_data_drain(c->buffer, *consumed); + + av_dlog(c->avr, "resampled %d in + %d leftover to %d out + %d leftover\n", + in_samples, in_leftover, out_samples, c->buffer->nb_samples); + + dst->nb_samples = out_samples; + return 0; +} + +int avresample_get_delay(AVAudioResampleContext *avr) +{ + if (!avr->resample_needed || !avr->resample) + return 0; + + return avr->resample->buffer->nb_samples; +} diff --git a/libavresample/resample.h b/libavresample/resample.h new file mode 100644 index 0000000000..b42fdbbaac --- /dev/null +++ b/libavresample/resample.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004 Michael Niedermayer + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_RESAMPLE_H +#define AVRESAMPLE_RESAMPLE_H + +#include "avresample.h" +#include "audio_data.h" + +typedef struct ResampleContext ResampleContext; + +/** + * Allocate and initialize a ResampleContext. + * + * The parameters in the AVAudioResampleContext are used to initialize the + * ResampleContext. + * + * @param avr AVAudioResampleContext + * @return newly-allocated ResampleContext + */ +ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr); + +/** + * Free a ResampleContext. + * + * @param c ResampleContext + */ +void ff_audio_resample_free(ResampleContext **c); + +/** + * Resample audio data. + * + * Changes the sample rate. + * + * @par + * All samples in the source data may not be consumed depending on the + * resampling parameters and the size of the output buffer. The unconsumed + * samples are automatically added to the start of the source in the next call. + * If the destination data can be reallocated, that may be done in this function + * in order to fit all available output. If it cannot be reallocated, fewer + * input samples will be consumed in order to have the output fit in the + * destination data buffers. + * + * @param c ResampleContext + * @param dst destination audio data + * @param src source audio data + * @param consumed number of samples consumed from the source + * @return number of samples written to the destination + */ +int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src, + int *consumed); + +#endif /* AVRESAMPLE_RESAMPLE_H */ diff --git a/libavresample/utils.c b/libavresample/utils.c new file mode 100644 index 0000000000..f54dcc6ae6 --- /dev/null +++ b/libavresample/utils.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/dict.h" +#include "libavutil/error.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "avresample.h" +#include "audio_data.h" +#include "internal.h" + +int avresample_open(AVAudioResampleContext *avr) +{ + int ret; + + /* set channel mixing parameters */ + avr->in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + if (avr->in_channels <= 0 || avr->in_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid input channel layout: %"PRIu64"\n", + avr->in_channel_layout); + return AVERROR(EINVAL); + } + avr->out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + if (avr->out_channels <= 0 || avr->out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid output channel layout: %"PRIu64"\n", + avr->out_channel_layout); + return AVERROR(EINVAL); + } + avr->resample_channels = FFMIN(avr->in_channels, avr->out_channels); + avr->downmix_needed = avr->in_channels > avr->out_channels; + avr->upmix_needed = avr->out_channels > avr->in_channels || + avr->am->matrix || + (avr->out_channels == avr->in_channels && + avr->in_channel_layout != avr->out_channel_layout); + avr->mixing_needed = avr->downmix_needed || avr->upmix_needed; + + /* set resampling parameters */ + avr->resample_needed = avr->in_sample_rate != avr->out_sample_rate || + avr->force_resampling; + + /* set sample format conversion parameters */ + /* override user-requested internal format to avoid unexpected failures + TODO: support more internal formats */ + if (avr->resample_needed && avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P) { + av_log(avr, AV_LOG_WARNING, "Using s16p as internal sample format\n"); + avr->internal_sample_fmt = AV_SAMPLE_FMT_S16P; + } else if (avr->mixing_needed && + avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P && + avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP) { + av_log(avr, AV_LOG_WARNING, "Using fltp as internal sample format\n"); + avr->internal_sample_fmt = AV_SAMPLE_FMT_FLTP; + } + if (avr->in_channels == 1) + avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt); + if (avr->out_channels == 1) + avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); + avr->in_convert_needed = (avr->resample_needed || avr->mixing_needed) && + avr->in_sample_fmt != avr->internal_sample_fmt; + if (avr->resample_needed || avr->mixing_needed) + avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt; + else + avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt; + + /* allocate buffers */ + if (avr->mixing_needed || avr->in_convert_needed) { + avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels), + 0, avr->internal_sample_fmt, + "in_buffer"); + if (!avr->in_buffer) { + ret = AVERROR(EINVAL); + goto error; + } + } + if (avr->resample_needed) { + avr->resample_out_buffer = ff_audio_data_alloc(avr->out_channels, + 0, avr->internal_sample_fmt, + "resample_out_buffer"); + if (!avr->resample_out_buffer) { + ret = AVERROR(EINVAL); + goto error; + } + } + if (avr->out_convert_needed) { + avr->out_buffer = ff_audio_data_alloc(avr->out_channels, 0, + avr->out_sample_fmt, "out_buffer"); + if (!avr->out_buffer) { + ret = AVERROR(EINVAL); + goto error; + } + } + avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, avr->out_channels, + 1024); + if (!avr->out_fifo) { + ret = AVERROR(ENOMEM); + goto error; + } + + /* setup contexts */ + if (avr->in_convert_needed) { + avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, + avr->in_sample_fmt, avr->in_channels); + if (!avr->ac_in) { + ret = AVERROR(ENOMEM); + goto error; + } + } + if (avr->out_convert_needed) { + enum AVSampleFormat src_fmt; + if (avr->in_convert_needed) + src_fmt = avr->internal_sample_fmt; + else + src_fmt = avr->in_sample_fmt; + avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, + avr->out_channels); + if (!avr->ac_out) { + ret = AVERROR(ENOMEM); + goto error; + } + } + if (avr->resample_needed) { + avr->resample = ff_audio_resample_init(avr); + if (!avr->resample) { + ret = AVERROR(ENOMEM); + goto error; + } + } + if (avr->mixing_needed) { + ret = ff_audio_mix_init(avr); + if (ret < 0) + goto error; + } + + return 0; + +error: + avresample_close(avr); + return ret; +} + +void avresample_close(AVAudioResampleContext *avr) +{ + ff_audio_data_free(&avr->in_buffer); + ff_audio_data_free(&avr->resample_out_buffer); + ff_audio_data_free(&avr->out_buffer); + av_audio_fifo_free(avr->out_fifo); + avr->out_fifo = NULL; + av_freep(&avr->ac_in); + av_freep(&avr->ac_out); + ff_audio_resample_free(&avr->resample); + ff_audio_mix_close(avr->am); + return; +} + +void avresample_free(AVAudioResampleContext **avr) +{ + if (!*avr) + return; + avresample_close(*avr); + av_freep(&(*avr)->am); + av_opt_free(*avr); + av_freep(avr); +} + +static int handle_buffered_output(AVAudioResampleContext *avr, + AudioData *output, AudioData *converted) +{ + int ret; + + if (!output || av_audio_fifo_size(avr->out_fifo) > 0 || + (converted && output->allocated_samples < converted->nb_samples)) { + if (converted) { + /* if there are any samples in the output FIFO or if the + user-supplied output buffer is not large enough for all samples, + we add to the output FIFO */ + av_dlog(avr, "[FIFO] add %s to out_fifo\n", converted->name); + ret = ff_audio_data_add_to_fifo(avr->out_fifo, converted, 0, + converted->nb_samples); + if (ret < 0) + return ret; + } + + /* if the user specified an output buffer, read samples from the output + FIFO to the user output */ + if (output && output->allocated_samples > 0) { + av_dlog(avr, "[FIFO] read from out_fifo to output\n"); + av_dlog(avr, "[end conversion]\n"); + return ff_audio_data_read_from_fifo(avr->out_fifo, output, + output->allocated_samples); + } + } else if (converted) { + /* copy directly to output if it is large enough or there is not any + data in the output FIFO */ + av_dlog(avr, "[copy] %s to output\n", converted->name); + output->nb_samples = 0; + ret = ff_audio_data_copy(output, converted); + if (ret < 0) + return ret; + av_dlog(avr, "[end conversion]\n"); + return output->nb_samples; + } + av_dlog(avr, "[end conversion]\n"); + return 0; +} + +int avresample_convert(AVAudioResampleContext *avr, void **output, + int out_plane_size, int out_samples, void **input, + int in_plane_size, int in_samples) +{ + AudioData input_buffer; + AudioData output_buffer; + AudioData *current_buffer; + int ret; + + /* reset internal buffers */ + if (avr->in_buffer) { + avr->in_buffer->nb_samples = 0; + ff_audio_data_set_channels(avr->in_buffer, + avr->in_buffer->allocated_channels); + } + if (avr->resample_out_buffer) { + avr->resample_out_buffer->nb_samples = 0; + ff_audio_data_set_channels(avr->resample_out_buffer, + avr->resample_out_buffer->allocated_channels); + } + if (avr->out_buffer) { + avr->out_buffer->nb_samples = 0; + ff_audio_data_set_channels(avr->out_buffer, + avr->out_buffer->allocated_channels); + } + + av_dlog(avr, "[start conversion]\n"); + + /* initialize output_buffer with output data */ + if (output) { + ret = ff_audio_data_init(&output_buffer, output, out_plane_size, + avr->out_channels, out_samples, + avr->out_sample_fmt, 0, "output"); + if (ret < 0) + return ret; + output_buffer.nb_samples = 0; + } + + if (input) { + /* initialize input_buffer with input data */ + ret = ff_audio_data_init(&input_buffer, input, in_plane_size, + avr->in_channels, in_samples, + avr->in_sample_fmt, 1, "input"); + if (ret < 0) + return ret; + current_buffer = &input_buffer; + + if (avr->upmix_needed && !avr->in_convert_needed && !avr->resample_needed && + !avr->out_convert_needed && output && out_samples >= in_samples) { + /* in some rare cases we can copy input to output and upmix + directly in the output buffer */ + av_dlog(avr, "[copy] %s to output\n", current_buffer->name); + ret = ff_audio_data_copy(&output_buffer, current_buffer); + if (ret < 0) + return ret; + current_buffer = &output_buffer; + } else if (avr->mixing_needed || avr->in_convert_needed) { + /* if needed, copy or convert input to in_buffer, and downmix if + applicable */ + if (avr->in_convert_needed) { + ret = ff_audio_data_realloc(avr->in_buffer, + current_buffer->nb_samples); + if (ret < 0) + return ret; + av_dlog(avr, "[convert] %s to in_buffer\n", current_buffer->name); + ret = ff_audio_convert(avr->ac_in, avr->in_buffer, current_buffer, + current_buffer->nb_samples); + if (ret < 0) + return ret; + } else { + av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name); + ret = ff_audio_data_copy(avr->in_buffer, current_buffer); + if (ret < 0) + return ret; + } + ff_audio_data_set_channels(avr->in_buffer, avr->in_channels); + if (avr->downmix_needed) { + av_dlog(avr, "[downmix] in_buffer\n"); + ret = ff_audio_mix(avr->am, avr->in_buffer); + if (ret < 0) + return ret; + } + current_buffer = avr->in_buffer; + } + } else { + /* flush resampling buffer and/or output FIFO if input is NULL */ + if (!avr->resample_needed) + return handle_buffered_output(avr, output ? &output_buffer : NULL, + NULL); + current_buffer = NULL; + } + + if (avr->resample_needed) { + AudioData *resample_out; + int consumed = 0; + + if (!avr->out_convert_needed && output && out_samples > 0) + resample_out = &output_buffer; + else + resample_out = avr->resample_out_buffer; + av_dlog(avr, "[resample] %s to %s\n", current_buffer->name, + resample_out->name); + ret = ff_audio_resample(avr->resample, resample_out, + current_buffer, &consumed); + if (ret < 0) + return ret; + + /* if resampling did not produce any samples, just return 0 */ + if (resample_out->nb_samples == 0) { + av_dlog(avr, "[end conversion]\n"); + return 0; + } + + current_buffer = resample_out; + } + + if (avr->upmix_needed) { + av_dlog(avr, "[upmix] %s\n", current_buffer->name); + ret = ff_audio_mix(avr->am, current_buffer); + if (ret < 0) + return ret; + } + + /* if we resampled or upmixed directly to output, return here */ + if (current_buffer == &output_buffer) { + av_dlog(avr, "[end conversion]\n"); + return current_buffer->nb_samples; + } + + if (avr->out_convert_needed) { + if (output && out_samples >= current_buffer->nb_samples) { + /* convert directly to output */ + av_dlog(avr, "[convert] %s to output\n", current_buffer->name); + ret = ff_audio_convert(avr->ac_out, &output_buffer, current_buffer, + current_buffer->nb_samples); + if (ret < 0) + return ret; + + av_dlog(avr, "[end conversion]\n"); + return output_buffer.nb_samples; + } else { + ret = ff_audio_data_realloc(avr->out_buffer, + current_buffer->nb_samples); + if (ret < 0) + return ret; + av_dlog(avr, "[convert] %s to out_buffer\n", current_buffer->name); + ret = ff_audio_convert(avr->ac_out, avr->out_buffer, + current_buffer, current_buffer->nb_samples); + if (ret < 0) + return ret; + current_buffer = avr->out_buffer; + } + } + + return handle_buffered_output(avr, &output_buffer, current_buffer); +} + +int avresample_available(AVAudioResampleContext *avr) +{ + return av_audio_fifo_size(avr->out_fifo); +} + +int avresample_read(AVAudioResampleContext *avr, void **output, int nb_samples) +{ + return av_audio_fifo_read(avr->out_fifo, output, nb_samples); +} + +unsigned avresample_version(void) +{ + return LIBAVRESAMPLE_VERSION_INT; +} + +const char *avresample_license(void) +{ +#define LICENSE_PREFIX "libavresample license: " + return LICENSE_PREFIX LIBAV_LICENSE + sizeof(LICENSE_PREFIX) - 1; +} + +const char *avresample_configuration(void) +{ + return LIBAV_CONFIGURATION; +} diff --git a/libavresample/version.h b/libavresample/version.h new file mode 100644 index 0000000000..4113edc21d --- /dev/null +++ b/libavresample/version.h @@ -0,0 +1,41 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_VERSION_H +#define AVRESAMPLE_VERSION_H + +#define LIBAVRESAMPLE_VERSION_MAJOR 0 +#define LIBAVRESAMPLE_VERSION_MINOR 0 +#define LIBAVRESAMPLE_VERSION_MICRO 0 + +#define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ + LIBAVRESAMPLE_VERSION_MINOR, \ + LIBAVRESAMPLE_VERSION_MICRO) +#define LIBAVRESAMPLE_VERSION AV_VERSION(LIBAVRESAMPLE_VERSION_MAJOR, \ + LIBAVRESAMPLE_VERSION_MINOR, \ + LIBAVRESAMPLE_VERSION_MICRO) +#define LIBAVRESAMPLE_BUILD LIBAVRESAMPLE_VERSION_INT + +#define LIBAVRESAMPLE_IDENT "Lavr" AV_STRINGIFY(LIBAVRESAMPLE_VERSION) + +/** + * These FF_API_* defines are not part of public API. + * They may change, break or disappear at any time. + */ + +#endif /* AVRESAMPLE_VERSION_H */ diff --git a/libavresample/x86/Makefile b/libavresample/x86/Makefile new file mode 100644 index 0000000000..63697faee7 --- /dev/null +++ b/libavresample/x86/Makefile @@ -0,0 +1,5 @@ +OBJS += x86/audio_convert_init.o \ + x86/audio_mix_init.o + +YASM-OBJS += x86/audio_convert.o \ + x86/audio_mix.o diff --git a/libavresample/x86/audio_convert.asm b/libavresample/x86/audio_convert.asm new file mode 100644 index 0000000000..809c5d1378 --- /dev/null +++ b/libavresample/x86/audio_convert.asm @@ -0,0 +1,104 @@ +;****************************************************************************** +;* x86 optimized Format Conversion Utils +;* Copyright (c) 2008 Loren Merritt +;* +;* This file is part of Libav. +;* +;* Libav 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. +;* +;* Libav 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 Libav; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%include "x86inc.asm" +%include "x86util.asm" + +SECTION_TEXT + +;----------------------------------------------------------------------------- +; void ff_conv_fltp_to_flt_6ch(float *dst, float *const *src, int len, +; int channels); +;----------------------------------------------------------------------------- + +%macro CONV_FLTP_TO_FLT_6CH 0 +cglobal conv_fltp_to_flt_6ch, 2,8,7, dst, src, src1, src2, src3, src4, src5, len +%if ARCH_X86_64 + mov lend, r2d +%else + %define lend dword r2m +%endif + mov src1q, [srcq+1*gprsize] + mov src2q, [srcq+2*gprsize] + mov src3q, [srcq+3*gprsize] + mov src4q, [srcq+4*gprsize] + mov src5q, [srcq+5*gprsize] + mov srcq, [srcq] + sub src1q, srcq + sub src2q, srcq + sub src3q, srcq + sub src4q, srcq + sub src5q, srcq +.loop: + mova m0, [srcq ] + mova m1, [srcq+src1q] + mova m2, [srcq+src2q] + mova m3, [srcq+src3q] + mova m4, [srcq+src4q] + mova m5, [srcq+src5q] +%if cpuflag(sse) + SBUTTERFLYPS 0, 1, 6 + SBUTTERFLYPS 2, 3, 6 + SBUTTERFLYPS 4, 5, 6 + + movaps m6, m4 + shufps m4, m0, q3210 + movlhps m0, m2 + movhlps m6, m2 + movaps [dstq ], m0 + movaps [dstq+16], m4 + movaps [dstq+32], m6 + + movaps m6, m5 + shufps m5, m1, q3210 + movlhps m1, m3 + movhlps m6, m3 + movaps [dstq+48], m1 + movaps [dstq+64], m5 + movaps [dstq+80], m6 +%else ; mmx + SBUTTERFLY dq, 0, 1, 6 + SBUTTERFLY dq, 2, 3, 6 + SBUTTERFLY dq, 4, 5, 6 + + movq [dstq ], m0 + movq [dstq+ 8], m2 + movq [dstq+16], m4 + movq [dstq+24], m1 + movq [dstq+32], m3 + movq [dstq+40], m5 +%endif + add srcq, mmsize + add dstq, mmsize*6 + sub lend, mmsize/4 + jg .loop +%if mmsize == 8 + emms + RET +%else + REP_RET +%endif +%endmacro + +INIT_MMX mmx +CONV_FLTP_TO_FLT_6CH +INIT_XMM sse +CONV_FLTP_TO_FLT_6CH diff --git a/libavresample/x86/audio_convert_init.c b/libavresample/x86/audio_convert_init.c new file mode 100644 index 0000000000..6883f10a21 --- /dev/null +++ b/libavresample/x86/audio_convert_init.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "libavutil/cpu.h" +#include "libavresample/audio_convert.h" + +extern void ff_conv_fltp_to_flt_6ch_mmx(float *dst, float *const *src, int len); +extern void ff_conv_fltp_to_flt_6ch_sse(float *dst, float *const *src, int len); + +av_cold void ff_audio_convert_init_x86(AudioConvert *ac) +{ +#if HAVE_YASM + int mm_flags = av_get_cpu_flags(); + + if (mm_flags & AV_CPU_FLAG_MMX && HAVE_MMX) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + 6, 1, 4, "MMX", ff_conv_fltp_to_flt_6ch_mmx); + } + if (mm_flags & AV_CPU_FLAG_SSE && HAVE_SSE) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + 6, 16, 4, "SSE", ff_conv_fltp_to_flt_6ch_sse); + } +#endif +} diff --git a/libavresample/x86/audio_mix.asm b/libavresample/x86/audio_mix.asm new file mode 100644 index 0000000000..ef30f02486 --- /dev/null +++ b/libavresample/x86/audio_mix.asm @@ -0,0 +1,64 @@ +;****************************************************************************** +;* x86 optimized channel mixing +;* Copyright (c) 2012 Justin Ruggles +;* +;* This file is part of Libav. +;* +;* Libav 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. +;* +;* Libav 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 Libav; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%include "x86inc.asm" +%include "x86util.asm" + +SECTION_TEXT + +;----------------------------------------------------------------------------- +; void ff_mix_2_to_1_fltp_flt(float **src, float **matrix, int len, +; int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +%macro MIX_2_TO_1_FLTP_FLT 0 +cglobal mix_2_to_1_fltp_flt, 3,4,6, src, matrix, len, src1 + mov src1q, [srcq+gprsize] + mov srcq, [srcq ] + sub src1q, srcq + mov matrixq, [matrixq ] + VBROADCASTSS m4, [matrixq ] + VBROADCASTSS m5, [matrixq+4] + ALIGN 16 +.loop: + mulps m0, m4, [srcq ] + mulps m1, m5, [srcq+src1q ] + mulps m2, m4, [srcq+ mmsize] + mulps m3, m5, [srcq+src1q+mmsize] + addps m0, m0, m1 + addps m2, m2, m3 + mova [srcq ], m0 + mova [srcq+mmsize], m2 + add srcq, mmsize*2 + sub lend, mmsize*2/4 + jg .loop +%if mmsize == 32 + vzeroupper + RET +%else + REP_RET +%endif +%endmacro + +INIT_XMM sse +MIX_2_TO_1_FLTP_FLT +INIT_YMM avx +MIX_2_TO_1_FLTP_FLT diff --git a/libavresample/x86/audio_mix_init.c b/libavresample/x86/audio_mix_init.c new file mode 100644 index 0000000000..8f8930f836 --- /dev/null +++ b/libavresample/x86/audio_mix_init.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "libavutil/cpu.h" +#include "libavresample/audio_mix.h" + +extern void ff_mix_2_to_1_fltp_flt_sse(float **src, float **matrix, int len, + int out_ch, int in_ch); +extern void ff_mix_2_to_1_fltp_flt_avx(float **src, float **matrix, int len, + int out_ch, int in_ch); + +av_cold void ff_audio_mix_init_x86(AudioMix *am) +{ +#if HAVE_YASM + int mm_flags = av_get_cpu_flags(); + + if (mm_flags & AV_CPU_FLAG_SSE && HAVE_SSE) { + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 16, 8, "SSE", ff_mix_2_to_1_fltp_flt_sse); + } + if (mm_flags & AV_CPU_FLAG_AVX && HAVE_AVX) { + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 32, 16, "AVX", ff_mix_2_to_1_fltp_flt_avx); + } +#endif +} diff --git a/libavutil/x86/x86util.asm b/libavutil/x86/x86util.asm index a9c75645c0..55f4a936e2 100644 --- a/libavutil/x86/x86util.asm +++ b/libavutil/x86/x86util.asm @@ -585,3 +585,12 @@ pminsd %1, %3 pmaxsd %1, %2 %endmacro + +%macro VBROADCASTSS 2 ; dst xmm/ymm, src m32 +%if cpuflag(avx) + vbroadcastss %1, %2 +%else ; sse + movss %1, %2 + shufps %1, %1, 0 +%endif +%endmacro -- cgit v1.2.3 From bcb82fe1f46ceb243b6e68e0e7b5766882024a28 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Thu, 5 Apr 2012 14:06:28 -0400 Subject: avconv: use libavresample --- avconv.c | 135 +++++++++++++++++++++++++++++-------------------------------- cmdutils.c | 5 ++- 2 files changed, 68 insertions(+), 72 deletions(-) diff --git a/avconv.c b/avconv.c index 6c3a01f6b6..5bd5121151 100644 --- a/avconv.c +++ b/avconv.c @@ -31,8 +31,8 @@ #include "libavformat/avformat.h" #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" +#include "libavresample/avresample.h" #include "libavutil/opt.h" -#include "libavcodec/audioconvert.h" #include "libavutil/audioconvert.h" #include "libavutil/parseutils.h" #include "libavutil/samplefmt.h" @@ -266,12 +266,11 @@ typedef struct OutputStream { /* audio only */ int audio_resample; - ReSampleContext *resample; /* for audio resampling */ + AVAudioResampleContext *avr; int resample_sample_fmt; int resample_channels; + uint64_t resample_channel_layout; int resample_sample_rate; - int reformat_pair; - AVAudioConvert *reformat_ctx; AVFifoBuffer *fifo; /* for compression: one audio fifo per codec */ FILE *logfile; @@ -1314,7 +1313,7 @@ static int encode_audio_frame(AVFormatContext *s, OutputStream *ost, } static int alloc_audio_output_buf(AVCodecContext *dec, AVCodecContext *enc, - int nb_samples) + int nb_samples, int *buf_linesize) { int64_t audio_buf_samples; int audio_buf_size; @@ -1327,7 +1326,7 @@ static int alloc_audio_output_buf(AVCodecContext *dec, AVCodecContext *enc, if (audio_buf_samples > INT_MAX) return AVERROR(EINVAL); - audio_buf_size = av_samples_get_buffer_size(NULL, enc->channels, + audio_buf_size = av_samples_get_buffer_size(buf_linesize, enc->channels, audio_buf_samples, enc->sample_fmt, 0); if (audio_buf_size < 0) @@ -1345,77 +1344,88 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost, { uint8_t *buftmp; - int size_out, frame_bytes, resample_changed; + int size_out, frame_bytes, resample_changed, ret; AVCodecContext *enc = ost->st->codec; AVCodecContext *dec = ist->st->codec; int osize = av_get_bytes_per_sample(enc->sample_fmt); int isize = av_get_bytes_per_sample(dec->sample_fmt); uint8_t *buf = decoded_frame->data[0]; int size = decoded_frame->nb_samples * dec->channels * isize; + int out_linesize = 0; + int buf_linesize = decoded_frame->linesize[0]; get_default_channel_layouts(ost, ist); - if (alloc_audio_output_buf(dec, enc, decoded_frame->nb_samples) < 0) { + if (alloc_audio_output_buf(dec, enc, decoded_frame->nb_samples, &out_linesize) < 0) { av_log(NULL, AV_LOG_FATAL, "Error allocating audio buffer\n"); exit_program(1); } - if (enc->channels != dec->channels || enc->sample_rate != dec->sample_rate) + if (audio_sync_method > 1 || + enc->channels != dec->channels || + enc->channel_layout != dec->channel_layout || + enc->sample_rate != dec->sample_rate || + dec->sample_fmt != enc->sample_fmt) ost->audio_resample = 1; resample_changed = ost->resample_sample_fmt != dec->sample_fmt || ost->resample_channels != dec->channels || + ost->resample_channel_layout != dec->channel_layout || ost->resample_sample_rate != dec->sample_rate; - if ((ost->audio_resample && !ost->resample) || resample_changed) { + if ((ost->audio_resample && !ost->avr) || 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", + av_log(NULL, AV_LOG_INFO, "Input stream #%d:%d frame changed from rate:%d fmt:%s ch:%d chl:0x%"PRIx64" to rate:%d fmt:%s ch:%d chl:0x%"PRIx64"\n", ist->file_index, ist->st->index, - ost->resample_sample_rate, av_get_sample_fmt_name(ost->resample_sample_fmt), ost->resample_channels, - dec->sample_rate, av_get_sample_fmt_name(dec->sample_fmt), dec->channels); + ost->resample_sample_rate, av_get_sample_fmt_name(ost->resample_sample_fmt), + ost->resample_channels, ost->resample_channel_layout, + dec->sample_rate, av_get_sample_fmt_name(dec->sample_fmt), + dec->channels, dec->channel_layout); ost->resample_sample_fmt = dec->sample_fmt; ost->resample_channels = dec->channels; + ost->resample_channel_layout = dec->channel_layout; ost->resample_sample_rate = dec->sample_rate; - if (ost->resample) - audio_resample_close(ost->resample); + if (ost->avr) + avresample_close(ost->avr); } /* 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_channel_layout == enc->channel_layout && ost->resample_sample_rate == enc->sample_rate) { - ost->resample = NULL; ost->audio_resample = 0; } else if (ost->audio_resample) { - if (dec->sample_fmt != AV_SAMPLE_FMT_S16) - av_log(NULL, AV_LOG_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) { - av_log(NULL, AV_LOG_FATAL, "Can not resample %d channels @ %d Hz to %d channels @ %d Hz\n", - dec->channels, dec->sample_rate, - enc->channels, enc->sample_rate); - exit_program(1); + if (!ost->avr) { + ost->avr = avresample_alloc_context(); + if (!ost->avr) { + av_log(NULL, AV_LOG_FATAL, "Error allocating context for libavresample\n"); + exit_program(1); + } } - } - } -#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) { - av_log(NULL, AV_LOG_FATAL, "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); + av_opt_set_int(ost->avr, "in_channel_layout", dec->channel_layout, 0); + av_opt_set_int(ost->avr, "in_sample_fmt", dec->sample_fmt, 0); + av_opt_set_int(ost->avr, "in_sample_rate", dec->sample_rate, 0); + av_opt_set_int(ost->avr, "out_channel_layout", enc->channel_layout, 0); + av_opt_set_int(ost->avr, "out_sample_fmt", enc->sample_fmt, 0); + av_opt_set_int(ost->avr, "out_sample_rate", enc->sample_rate, 0); + if (audio_sync_method > 1) + av_opt_set_int(ost->avr, "force_resampling", 1, 0); + + /* if both the input and output formats are s16 or u8, use s16 as + the internal sample format */ + if (av_get_bytes_per_sample(dec->sample_fmt) <= 2 && + av_get_bytes_per_sample(enc->sample_fmt) <= 2) { + av_opt_set_int(ost->avr, "internal_sample_fmt", AV_SAMPLE_FMT_S16P, 0); + } + + ret = avresample_open(ost->avr); + if (ret < 0) { + av_log(NULL, AV_LOG_FATAL, "Error opening libavresample\n"); + exit_program(1); + } } - ost->reformat_pair = MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt); } if (audio_sync_method > 0) { @@ -1444,7 +1454,7 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost, exit_program(1); } - if (alloc_audio_output_buf(dec, enc, decoded_frame->nb_samples + idelta) < 0) { + if (alloc_audio_output_buf(dec, enc, decoded_frame->nb_samples + idelta, &out_linesize) < 0) { av_log(NULL, AV_LOG_FATAL, "Error allocating audio buffer\n"); exit_program(1); } @@ -1454,15 +1464,15 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost, memcpy(async_buf + byte_delta, buf, size); buf = async_buf; size += byte_delta; + buf_linesize = allocated_async_buf_size; av_log(NULL, AV_LOG_VERBOSE, "adding %d audio samples of silence\n", idelta); } } else if (audio_sync_method > 1) { int comp = av_clip(delta, -audio_sync_method, audio_sync_method); - av_assert0(ost->audio_resample); av_log(NULL, AV_LOG_VERBOSE, "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); + avresample_set_compensation(ost->avr, comp, enc->sample_rate); } } } else if (audio_sync_method == 0) @@ -1471,31 +1481,16 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost, if (ost->audio_resample) { buftmp = audio_buf; - size_out = audio_resample(ost->resample, - (short *)buftmp, (short *)buf, - size / (dec->channels * isize)); + size_out = avresample_convert(ost->avr, (void **)&buftmp, + allocated_audio_buf_size, out_linesize, + (void **)&buf, buf_linesize, + 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; - } - /* now encode as many frames as possible */ if (!(enc->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)) { /* output resampled raw samples */ @@ -2709,7 +2704,6 @@ static int transcode_init(void) 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; @@ -2722,15 +2716,16 @@ static int transcode_init(void) if (!codec->channels) codec->channels = icodec->channels; - codec->channel_layout = icodec->channel_layout; + if (!codec->channel_layout) + codec->channel_layout = icodec->channel_layout; 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; icodec->request_channels = codec-> channels; ost->resample_sample_fmt = icodec->sample_fmt; ost->resample_sample_rate = icodec->sample_rate; ost->resample_channels = icodec->channels; + ost->resample_channel_layout = icodec->channel_layout; break; case AVMEDIA_TYPE_VIDEO: if (!ost->filter) { @@ -3202,10 +3197,8 @@ static int transcode(void) initialized but set to zero */ av_freep(&ost->st->codec->subtitle_header); av_free(ost->forced_kf_pts); - if (ost->resample) - audio_resample_close(ost->resample); - if (ost->reformat_ctx) - av_audio_convert_free(ost->reformat_ctx); + if (ost->avr) + avresample_free(&ost->avr); av_dict_free(&ost->opts); } } diff --git a/cmdutils.c b/cmdutils.c index d590d0a49f..6d2e97f694 100644 --- a/cmdutils.c +++ b/cmdutils.c @@ -32,6 +32,7 @@ #include "libavformat/avformat.h" #include "libavfilter/avfilter.h" #include "libavdevice/avdevice.h" +#include "libavresample/avresample.h" #include "libswscale/swscale.h" #include "libavutil/avstring.h" #include "libavutil/mathematics.h" @@ -460,7 +461,8 @@ static int warned_cfg = 0; const char *indent = flags & INDENT? " " : ""; \ if (flags & SHOW_VERSION) { \ unsigned int version = libname##_version(); \ - av_log(NULL, level, "%slib%-9s %2d.%3d.%2d / %2d.%3d.%2d\n",\ + av_log(NULL, level, \ + "%slib%-10s %2d.%3d.%2d / %2d.%3d.%2d\n", \ indent, #libname, \ LIB##LIBNAME##_VERSION_MAJOR, \ LIB##LIBNAME##_VERSION_MINOR, \ @@ -489,6 +491,7 @@ static void print_all_libs_info(int flags, int level) PRINT_LIB_INFO(avformat, AVFORMAT, flags, level); PRINT_LIB_INFO(avdevice, AVDEVICE, flags, level); PRINT_LIB_INFO(avfilter, AVFILTER, flags, level); + PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level); PRINT_LIB_INFO(swscale, SWSCALE, flags, level); } -- cgit v1.2.3 From 394dbde5484507f213768019623d016196ddad5f Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Mon, 23 Apr 2012 14:03:25 -0400 Subject: FATE: use updated reference for aac-latm_stereo_to_51 This uses correct stereo to 5.1 upmixing via libavresample. --- tests/fate/aac.mak | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fate/aac.mak b/tests/fate/aac.mak index 0d4aa4a4fb..1975e9eb08 100644 --- a/tests/fate/aac.mak +++ b/tests/fate/aac.mak @@ -55,8 +55,8 @@ fate-aac-ap05_48: CMD = pcm -i $(SAMPLES)/aac/ap05_48.mp4 fate-aac-ap05_48: REF = $(SAMPLES)/aac/ap05_48.s16 FATE_AAC += fate-aac-latm_stereo_to_51 -fate-aac-latm_stereo_to_51: CMD = pcm -i $(SAMPLES)/aac/latm_stereo_to_51.ts -ac 6 -fate-aac-latm_stereo_to_51: REF = $(SAMPLES)/aac/latm_stereo_to_51.s16 +fate-aac-latm_stereo_to_51: CMD = pcm -i $(SAMPLES)/aac/latm_stereo_to_51.ts -channel_layout 5.1 +fate-aac-latm_stereo_to_51: REF = $(SAMPLES)/aac/latm_stereo_to_51_ref.s16 fate-aac-ct%: CMD = pcm -i $(SAMPLES)/aac/CT_DecoderCheck/$(@:fate-aac-ct-%=%) fate-aac-ct%: REF = $(SAMPLES)/aac/CT_DecoderCheck/aacPlusv2.wav -- cgit v1.2.3