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