mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
790f793844
There are lots of files that don't need it: The number of object files that actually need it went down from 2011 to 884 here. Keep it for external users in order to not cause breakages. Also improve the other headers a bit while just at it. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
957 lines
33 KiB
C
957 lines
33 KiB
C
/*
|
|
* Copyright (C) 2011-2013 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/mem.h"
|
|
#include "libavutil/opt.h"
|
|
#include "swresample_internal.h"
|
|
#include "audioconvert.h"
|
|
#include "libavutil/avassert.h"
|
|
#include "libavutil/channel_layout.h"
|
|
#include "libavutil/internal.h"
|
|
|
|
#include <float.h>
|
|
|
|
#define ALIGN 32
|
|
|
|
int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map){
|
|
if(!s || s->in_convert) // s needs to be allocated but not initialized
|
|
return AVERROR(EINVAL);
|
|
s->channel_map = channel_map;
|
|
return 0;
|
|
}
|
|
|
|
int swr_alloc_set_opts2(struct SwrContext **ps,
|
|
const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
|
|
const AVChannelLayout *in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
|
|
int log_offset, void *log_ctx) {
|
|
struct SwrContext *s = *ps;
|
|
int ret;
|
|
|
|
if (!s) s = swr_alloc();
|
|
if (!s) return AVERROR(ENOMEM);
|
|
|
|
*ps = s;
|
|
|
|
s->log_level_offset = log_offset;
|
|
s->log_ctx = log_ctx;
|
|
|
|
if ((ret = av_opt_set_chlayout(s, "ochl", out_ch_layout, 0)) < 0)
|
|
goto fail;
|
|
|
|
if ((ret = av_opt_set_int(s, "osf", out_sample_fmt, 0)) < 0)
|
|
goto fail;
|
|
|
|
if ((ret = av_opt_set_int(s, "osr", out_sample_rate, 0)) < 0)
|
|
goto fail;
|
|
|
|
if ((ret = av_opt_set_chlayout(s, "ichl", in_ch_layout, 0)) < 0)
|
|
goto fail;
|
|
|
|
if ((ret = av_opt_set_int(s, "isf", in_sample_fmt, 0)) < 0)
|
|
goto fail;
|
|
|
|
if ((ret = av_opt_set_int(s, "isr", in_sample_rate, 0)) < 0)
|
|
goto fail;
|
|
|
|
av_opt_set_int(s, "uch", 0, 0);
|
|
|
|
return 0;
|
|
fail:
|
|
av_log(s, AV_LOG_ERROR, "Failed to set option\n");
|
|
swr_free(ps);
|
|
return ret;
|
|
}
|
|
|
|
static void set_audiodata_fmt(AudioData *a, enum AVSampleFormat fmt){
|
|
a->fmt = fmt;
|
|
a->bps = av_get_bytes_per_sample(fmt);
|
|
a->planar= av_sample_fmt_is_planar(fmt);
|
|
if (a->ch_count == 1)
|
|
a->planar = 1;
|
|
}
|
|
|
|
static void free_temp(AudioData *a){
|
|
av_free(a->data);
|
|
memset(a, 0, sizeof(*a));
|
|
}
|
|
|
|
static void clear_context(SwrContext *s){
|
|
s->in_buffer_index= 0;
|
|
s->in_buffer_count= 0;
|
|
s->resample_in_constraint= 0;
|
|
memset(s->in.ch, 0, sizeof(s->in.ch));
|
|
memset(s->out.ch, 0, sizeof(s->out.ch));
|
|
free_temp(&s->postin);
|
|
free_temp(&s->midbuf);
|
|
free_temp(&s->preout);
|
|
free_temp(&s->in_buffer);
|
|
free_temp(&s->silence);
|
|
free_temp(&s->drop_temp);
|
|
free_temp(&s->dither.noise);
|
|
free_temp(&s->dither.temp);
|
|
av_channel_layout_uninit(&s->in_ch_layout);
|
|
av_channel_layout_uninit(&s->out_ch_layout);
|
|
av_channel_layout_uninit(&s->used_ch_layout);
|
|
swri_audio_convert_free(&s-> in_convert);
|
|
swri_audio_convert_free(&s->out_convert);
|
|
swri_audio_convert_free(&s->full_convert);
|
|
swri_rematrix_free(s);
|
|
|
|
s->delayed_samples_fixup = 0;
|
|
s->flushed = 0;
|
|
}
|
|
|
|
av_cold void swr_free(SwrContext **ss){
|
|
SwrContext *s= *ss;
|
|
if(s){
|
|
clear_context(s);
|
|
av_channel_layout_uninit(&s->user_in_chlayout);
|
|
av_channel_layout_uninit(&s->user_out_chlayout);
|
|
av_channel_layout_uninit(&s->user_used_chlayout);
|
|
|
|
if (s->resampler)
|
|
s->resampler->free(&s->resample);
|
|
}
|
|
|
|
av_freep(ss);
|
|
}
|
|
|
|
av_cold void swr_close(SwrContext *s){
|
|
clear_context(s);
|
|
}
|
|
|
|
av_cold int swr_init(struct SwrContext *s){
|
|
int ret;
|
|
char l1[1024], l2[1024];
|
|
|
|
clear_context(s);
|
|
|
|
if((unsigned) s-> in_sample_fmt >= AV_SAMPLE_FMT_NB){
|
|
av_log(s, AV_LOG_ERROR, "Requested input sample format %d is invalid\n", s->in_sample_fmt);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
if((unsigned) s->out_sample_fmt >= AV_SAMPLE_FMT_NB){
|
|
av_log(s, AV_LOG_ERROR, "Requested output sample format %d is invalid\n", s->out_sample_fmt);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if(s-> in_sample_rate <= 0){
|
|
av_log(s, AV_LOG_ERROR, "Requested input sample rate %d is invalid\n", s->in_sample_rate);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
if(s->out_sample_rate <= 0){
|
|
av_log(s, AV_LOG_ERROR, "Requested output sample rate %d is invalid\n", s->out_sample_rate);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
s->out.ch_count = s-> user_out_chlayout.nb_channels;
|
|
s-> in.ch_count = s-> user_in_chlayout.nb_channels;
|
|
|
|
if (!(ret = av_channel_layout_check(&s->user_in_chlayout)) || s->user_in_chlayout.nb_channels > SWR_CH_MAX) {
|
|
if (ret)
|
|
av_channel_layout_describe(&s->user_in_chlayout, l1, sizeof(l1));
|
|
av_log(s, AV_LOG_WARNING, "Input channel layout \"%s\" is invalid or unsupported.\n", ret ? l1 : "");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (!(ret = av_channel_layout_check(&s->user_out_chlayout)) || s->user_out_chlayout.nb_channels > SWR_CH_MAX) {
|
|
if (ret)
|
|
av_channel_layout_describe(&s->user_out_chlayout, l2, sizeof(l2));
|
|
av_log(s, AV_LOG_WARNING, "Output channel layout \"%s\" is invalid or unsupported.\n", ret ? l2 : "");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
ret = av_channel_layout_copy(&s->in_ch_layout, &s->user_in_chlayout);
|
|
ret |= av_channel_layout_copy(&s->out_ch_layout, &s->user_out_chlayout);
|
|
ret |= av_channel_layout_copy(&s->used_ch_layout, &s->user_used_chlayout);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
s->int_sample_fmt= s->user_int_sample_fmt;
|
|
|
|
s->dither.method = s->user_dither_method;
|
|
|
|
switch(s->engine){
|
|
#if CONFIG_LIBSOXR
|
|
case SWR_ENGINE_SOXR: s->resampler = &swri_soxr_resampler; break;
|
|
#endif
|
|
case SWR_ENGINE_SWR : s->resampler = &swri_resampler; break;
|
|
default:
|
|
av_log(s, AV_LOG_ERROR, "Requested resampling engine is unavailable\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (!av_channel_layout_check(&s->used_ch_layout))
|
|
av_channel_layout_default(&s->used_ch_layout, s->in.ch_count);
|
|
|
|
if (s->used_ch_layout.nb_channels != s->in_ch_layout.nb_channels)
|
|
av_channel_layout_uninit(&s->in_ch_layout);
|
|
|
|
if (s->used_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
|
|
av_channel_layout_default(&s->used_ch_layout, s->used_ch_layout.nb_channels);
|
|
if (s->in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
|
|
ret = av_channel_layout_copy(&s->in_ch_layout, &s->used_ch_layout);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
if (s->out_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC)
|
|
av_channel_layout_default(&s->out_ch_layout, s->out.ch_count);
|
|
|
|
s->rematrix = av_channel_layout_compare(&s->out_ch_layout, &s->in_ch_layout) ||
|
|
s->rematrix_volume!=1.0 ||
|
|
s->rematrix_custom;
|
|
|
|
if(s->int_sample_fmt == AV_SAMPLE_FMT_NONE){
|
|
// 16bit or less to 16bit or less with the same sample rate
|
|
if( av_get_bytes_per_sample(s-> in_sample_fmt) <= 2
|
|
&& av_get_bytes_per_sample(s->out_sample_fmt) <= 2
|
|
&& s->out_sample_rate==s->in_sample_rate) {
|
|
s->int_sample_fmt= AV_SAMPLE_FMT_S16P;
|
|
// 8 -> 8, 16->8, 8->16bit
|
|
} else if( av_get_bytes_per_sample(s-> in_sample_fmt)
|
|
+av_get_bytes_per_sample(s->out_sample_fmt) <= 3 ) {
|
|
s->int_sample_fmt= AV_SAMPLE_FMT_S16P;
|
|
}else if( av_get_bytes_per_sample(s-> in_sample_fmt) <= 2
|
|
&& !s->rematrix
|
|
&& s->out_sample_rate==s->in_sample_rate
|
|
&& !(s->flags & SWR_FLAG_RESAMPLE)){
|
|
s->int_sample_fmt= AV_SAMPLE_FMT_S16P;
|
|
}else if( av_get_planar_sample_fmt(s-> in_sample_fmt) == AV_SAMPLE_FMT_S32P
|
|
&& av_get_planar_sample_fmt(s->out_sample_fmt) == AV_SAMPLE_FMT_S32P
|
|
&& !s->rematrix
|
|
&& s->out_sample_rate == s->in_sample_rate
|
|
&& !(s->flags & SWR_FLAG_RESAMPLE)
|
|
&& s->engine != SWR_ENGINE_SOXR){
|
|
s->int_sample_fmt= AV_SAMPLE_FMT_S32P;
|
|
}else if(av_get_bytes_per_sample(s->in_sample_fmt) <= 4){
|
|
s->int_sample_fmt= AV_SAMPLE_FMT_FLTP;
|
|
}else{
|
|
s->int_sample_fmt= AV_SAMPLE_FMT_DBLP;
|
|
}
|
|
}
|
|
av_log(s, AV_LOG_DEBUG, "Using %s internally between filters\n", av_get_sample_fmt_name(s->int_sample_fmt));
|
|
|
|
if( s->int_sample_fmt != AV_SAMPLE_FMT_S16P
|
|
&&s->int_sample_fmt != AV_SAMPLE_FMT_S32P
|
|
&&s->int_sample_fmt != AV_SAMPLE_FMT_S64P
|
|
&&s->int_sample_fmt != AV_SAMPLE_FMT_FLTP
|
|
&&s->int_sample_fmt != AV_SAMPLE_FMT_DBLP){
|
|
av_log(s, AV_LOG_ERROR, "Requested sample format %s is not supported internally, s16p/s32p/s64p/fltp/dblp are supported\n", av_get_sample_fmt_name(s->int_sample_fmt));
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
set_audiodata_fmt(&s-> in, s-> in_sample_fmt);
|
|
set_audiodata_fmt(&s->out, s->out_sample_fmt);
|
|
|
|
if (s->firstpts_in_samples != AV_NOPTS_VALUE) {
|
|
if (!s->async && s->min_compensation >= FLT_MAX/2)
|
|
s->async = 1;
|
|
if (s->firstpts == AV_NOPTS_VALUE)
|
|
s->firstpts =
|
|
s->outpts = s->firstpts_in_samples * s->out_sample_rate;
|
|
} else
|
|
s->firstpts = AV_NOPTS_VALUE;
|
|
|
|
if (s->async) {
|
|
if (s->min_compensation >= FLT_MAX/2)
|
|
s->min_compensation = 0.001;
|
|
if (s->async > 1.0001) {
|
|
s->max_soft_compensation = s->async / (double) s->in_sample_rate;
|
|
}
|
|
}
|
|
|
|
if (s->out_sample_rate!=s->in_sample_rate || (s->flags & SWR_FLAG_RESAMPLE)){
|
|
s->resample = s->resampler->init(s->resample, s->out_sample_rate, s->in_sample_rate, s->filter_size, s->phase_shift, s->linear_interp, s->cutoff, s->int_sample_fmt, s->filter_type, s->kaiser_beta, s->precision, s->cheby, s->exact_rational);
|
|
if (!s->resample) {
|
|
av_log(s, AV_LOG_ERROR, "Failed to initialize resampler\n");
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
}else
|
|
s->resampler->free(&s->resample);
|
|
if( s->int_sample_fmt != AV_SAMPLE_FMT_S16P
|
|
&& s->int_sample_fmt != AV_SAMPLE_FMT_S32P
|
|
&& s->int_sample_fmt != AV_SAMPLE_FMT_FLTP
|
|
&& s->int_sample_fmt != AV_SAMPLE_FMT_DBLP
|
|
&& s->resample){
|
|
av_log(s, AV_LOG_ERROR, "Resampling only supported with internal s16p/s32p/fltp/dblp\n");
|
|
ret = AVERROR(EINVAL);
|
|
goto fail;
|
|
}
|
|
|
|
#define RSC 1 //FIXME finetune
|
|
if(!s-> in.ch_count)
|
|
s-> in.ch_count = s->in_ch_layout.nb_channels;
|
|
if (!av_channel_layout_check(&s->used_ch_layout))
|
|
av_channel_layout_default(&s->used_ch_layout, s->in.ch_count);
|
|
if(!s->out.ch_count)
|
|
s->out.ch_count = s->out_ch_layout.nb_channels;
|
|
|
|
if(!s-> in.ch_count){
|
|
av_assert0(s->in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC);
|
|
av_log(s, AV_LOG_ERROR, "Input channel count and layout are unset\n");
|
|
ret = AVERROR(EINVAL);
|
|
goto fail;
|
|
}
|
|
|
|
av_channel_layout_describe(&s->out_ch_layout, l2, sizeof(l2));
|
|
av_channel_layout_describe(&s->in_ch_layout, l1, sizeof(l1));
|
|
if (s->in_ch_layout.order != AV_CHANNEL_ORDER_UNSPEC && s->used_ch_layout.nb_channels != s->in_ch_layout.nb_channels) {
|
|
av_log(s, AV_LOG_ERROR, "Input channel layout %s mismatches specified channel count %d\n", l1, s->used_ch_layout.nb_channels);
|
|
ret = AVERROR(EINVAL);
|
|
goto fail;
|
|
}
|
|
|
|
if (( s->out_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC
|
|
|| s-> in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) && s->used_ch_layout.nb_channels != s->out.ch_count && !s->rematrix_custom) {
|
|
av_log(s, AV_LOG_ERROR, "Rematrix is needed between %s and %s "
|
|
"but there is not enough information to do it\n", l1, l2);
|
|
ret = AVERROR(EINVAL);
|
|
goto fail;
|
|
}
|
|
|
|
av_assert0(s->used_ch_layout.nb_channels);
|
|
av_assert0(s->out.ch_count);
|
|
s->resample_first= RSC*s->out.ch_count/s->used_ch_layout.nb_channels - RSC < s->out_sample_rate/(float)s-> in_sample_rate - 1.0;
|
|
|
|
s->in_buffer= s->in;
|
|
s->silence = s->in;
|
|
s->drop_temp= s->out;
|
|
|
|
if ((ret = swri_dither_init(s, s->out_sample_fmt, s->int_sample_fmt)) < 0)
|
|
goto fail;
|
|
|
|
if(!s->resample && !s->rematrix && !s->channel_map && !s->dither.method){
|
|
s->full_convert = swri_audio_convert_alloc(s->out_sample_fmt,
|
|
s-> in_sample_fmt, s-> in.ch_count, NULL, 0);
|
|
return 0;
|
|
}
|
|
|
|
s->in_convert = swri_audio_convert_alloc(s->int_sample_fmt,
|
|
s-> in_sample_fmt, s->used_ch_layout.nb_channels, s->channel_map, 0);
|
|
s->out_convert= swri_audio_convert_alloc(s->out_sample_fmt,
|
|
s->int_sample_fmt, s->out.ch_count, NULL, 0);
|
|
|
|
if (!s->in_convert || !s->out_convert) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
s->postin= s->in;
|
|
s->preout= s->out;
|
|
s->midbuf= s->in;
|
|
|
|
if(s->channel_map){
|
|
s->postin.ch_count=
|
|
s->midbuf.ch_count= s->used_ch_layout.nb_channels;
|
|
if(s->resample)
|
|
s->in_buffer.ch_count= s->used_ch_layout.nb_channels;
|
|
}
|
|
if(!s->resample_first){
|
|
s->midbuf.ch_count= s->out.ch_count;
|
|
if(s->resample)
|
|
s->in_buffer.ch_count = s->out.ch_count;
|
|
}
|
|
|
|
set_audiodata_fmt(&s->postin, s->int_sample_fmt);
|
|
set_audiodata_fmt(&s->midbuf, s->int_sample_fmt);
|
|
set_audiodata_fmt(&s->preout, s->int_sample_fmt);
|
|
|
|
if(s->resample){
|
|
set_audiodata_fmt(&s->in_buffer, s->int_sample_fmt);
|
|
}
|
|
|
|
av_assert0(!s->preout.count);
|
|
s->dither.noise = s->preout;
|
|
s->dither.temp = s->preout;
|
|
if (s->dither.method > SWR_DITHER_NS) {
|
|
s->dither.noise.bps = 4;
|
|
s->dither.noise.fmt = AV_SAMPLE_FMT_FLTP;
|
|
s->dither.noise_scale = 1;
|
|
}
|
|
|
|
if(s->rematrix || s->dither.method) {
|
|
ret = swri_rematrix_init(s);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
|
|
return 0;
|
|
fail:
|
|
swr_close(s);
|
|
return ret;
|
|
|
|
}
|
|
|
|
int swri_realloc_audio(AudioData *a, int count){
|
|
int i, countb;
|
|
AudioData old;
|
|
|
|
if(count < 0 || count > INT_MAX/2/a->bps/a->ch_count)
|
|
return AVERROR(EINVAL);
|
|
|
|
if(a->count >= count)
|
|
return 0;
|
|
|
|
count*=2;
|
|
|
|
countb= FFALIGN(count*a->bps, ALIGN);
|
|
old= *a;
|
|
|
|
av_assert0(a->bps);
|
|
av_assert0(a->ch_count);
|
|
|
|
a->data = av_calloc(countb, a->ch_count);
|
|
if(!a->data)
|
|
return AVERROR(ENOMEM);
|
|
for(i=0; i<a->ch_count; i++){
|
|
a->ch[i]= a->data + i*(a->planar ? countb : a->bps);
|
|
if(a->count && a->planar) memcpy(a->ch[i], old.ch[i], a->count*a->bps);
|
|
}
|
|
if(a->count && !a->planar) memcpy(a->ch[0], old.ch[0], a->count*a->ch_count*a->bps);
|
|
av_freep(&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; ch<out->ch_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);
|
|
}
|
|
|
|
static void fill_audiodata(AudioData *out, uint8_t *const in_arg [SWR_CH_MAX])
|
|
{
|
|
int i;
|
|
if(!in_arg){
|
|
memset(out->ch, 0, sizeof(out->ch));
|
|
}else if(out->planar){
|
|
for(i=0; i<out->ch_count; i++)
|
|
out->ch[i]= in_arg[i];
|
|
}else{
|
|
for(i=0; i<out->ch_count; i++)
|
|
out->ch[i]= in_arg[0] + i*out->bps;
|
|
}
|
|
}
|
|
|
|
static void reversefill_audiodata(AudioData *out, uint8_t *in_arg [SWR_CH_MAX]){
|
|
int i;
|
|
if(out->planar){
|
|
for(i=0; i<out->ch_count; i++)
|
|
in_arg[i]= out->ch[i];
|
|
}else{
|
|
in_arg[0]= out->ch[0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* out may be equal in.
|
|
*/
|
|
static void buf_set(AudioData *out, AudioData *in, int count){
|
|
int ch;
|
|
if(in->planar){
|
|
for(ch=0; ch<out->ch_count; ch++)
|
|
out->ch[ch]= in->ch[ch] + count*out->bps;
|
|
}else{
|
|
for(ch=out->ch_count-1; ch>=0; ch--)
|
|
out->ch[ch]= in->ch[0] + (ch + 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 padless = ARCH_X86 && s->engine == SWR_ENGINE_SWR ? 7 : 0;
|
|
|
|
av_assert1(s->in_buffer.ch_count == in_param->ch_count);
|
|
av_assert1(s->in_buffer.planar == in_param->planar);
|
|
av_assert1(s->in_buffer.fmt == in_param->fmt);
|
|
|
|
tmp=out=*out_param;
|
|
in = *in_param;
|
|
|
|
border = s->resampler->invert_initial_buffer(s->resample, &s->in_buffer,
|
|
&in, in_count, &s->in_buffer_index, &s->in_buffer_count);
|
|
if (border == INT_MAX) {
|
|
return 0;
|
|
} else if (border < 0) {
|
|
return border;
|
|
} else if (border) {
|
|
buf_set(&in, &in, border);
|
|
in_count -= border;
|
|
s->resample_in_constraint = 0;
|
|
}
|
|
|
|
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= s->resampler->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((s->flushed || in_count > padless) && !s->in_buffer_count){
|
|
s->in_buffer_index=0;
|
|
ret= s->resampler->multiple_resample(s->resample, &out, out_count, &in, FFMAX(in_count-padless, 0), &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=swri_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;
|
|
if (padless) {
|
|
padless = 0;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}while(1);
|
|
|
|
s->resample_in_constraint= !!out_count;
|
|
|
|
return ret_sum;
|
|
}
|
|
|
|
static int swr_convert_internal(struct SwrContext *s, AudioData *out, int out_count,
|
|
AudioData *in , int in_count){
|
|
AudioData *postin, *midbuf, *preout;
|
|
int ret/*, in_max*/;
|
|
AudioData preout_tmp, midbuf_tmp;
|
|
|
|
if(s->full_convert){
|
|
av_assert0(!s->resample);
|
|
swri_audio_convert(s->full_convert, out, in, in_count);
|
|
return out_count;
|
|
}
|
|
|
|
// 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=swri_realloc_audio(&s->postin, in_count))<0)
|
|
return ret;
|
|
if(s->resample_first){
|
|
av_assert0(s->midbuf.ch_count == s->used_ch_layout.nb_channels);
|
|
if((ret=swri_realloc_audio(&s->midbuf, out_count))<0)
|
|
return ret;
|
|
}else{
|
|
av_assert0(s->midbuf.ch_count == s->out.ch_count);
|
|
if((ret=swri_realloc_audio(&s->midbuf, in_count))<0)
|
|
return ret;
|
|
}
|
|
if((ret=swri_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 && !s->channel_map)
|
|
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
|
|
&& !(s->out_sample_fmt==AV_SAMPLE_FMT_S32P && (s->dither.output_sample_bits&31))){
|
|
if(preout==in){
|
|
out_count= FFMIN(out_count, in_count); //TODO check at the 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){
|
|
swri_audio_convert(s->in_convert, postin, in, in_count);
|
|
}
|
|
|
|
if(s->resample_first){
|
|
if(postin != midbuf)
|
|
if ((out_count = resample(s, midbuf, out_count, postin, in_count)) < 0)
|
|
return out_count;
|
|
if(midbuf != preout)
|
|
swri_rematrix(s, preout, midbuf, out_count, preout==out);
|
|
}else{
|
|
if(postin != midbuf)
|
|
swri_rematrix(s, midbuf, postin, in_count, midbuf==out);
|
|
if(midbuf != preout)
|
|
if ((out_count = resample(s, preout, out_count, midbuf, in_count)) < 0)
|
|
return out_count;
|
|
}
|
|
|
|
if(preout != out && out_count){
|
|
AudioData *conv_src = preout;
|
|
if(s->dither.method){
|
|
int ch;
|
|
int dither_count= FFMAX(out_count, 1<<16);
|
|
|
|
if (preout == in) {
|
|
conv_src = &s->dither.temp;
|
|
if((ret=swri_realloc_audio(&s->dither.temp, dither_count))<0)
|
|
return ret;
|
|
}
|
|
|
|
if((ret=swri_realloc_audio(&s->dither.noise, dither_count))<0)
|
|
return ret;
|
|
if(ret)
|
|
for(ch=0; ch<s->dither.noise.ch_count; ch++)
|
|
if((ret=swri_get_dither(s, s->dither.noise.ch[ch], s->dither.noise.count, (12345678913579ULL*ch + 3141592) % 2718281828U, s->dither.noise.fmt))<0)
|
|
return ret;
|
|
av_assert0(s->dither.noise.ch_count == preout->ch_count);
|
|
|
|
if(s->dither.noise_pos + out_count > s->dither.noise.count)
|
|
s->dither.noise_pos = 0;
|
|
|
|
if (s->dither.method < SWR_DITHER_NS){
|
|
if (s->mix_2_1_simd) {
|
|
int len1= out_count&~15;
|
|
int off = len1 * preout->bps;
|
|
|
|
if(len1)
|
|
for(ch=0; ch<preout->ch_count; ch++)
|
|
s->mix_2_1_simd(conv_src->ch[ch], preout->ch[ch], s->dither.noise.ch[ch] + s->dither.noise.bps * s->dither.noise_pos, s->native_simd_one, 0, 0, len1);
|
|
if(out_count != len1)
|
|
for(ch=0; ch<preout->ch_count; ch++)
|
|
s->mix_2_1_f(conv_src->ch[ch] + off, preout->ch[ch] + off, s->dither.noise.ch[ch] + s->dither.noise.bps * s->dither.noise_pos + off, s->native_one, 0, 0, out_count - len1);
|
|
} else {
|
|
for(ch=0; ch<preout->ch_count; ch++)
|
|
s->mix_2_1_f(conv_src->ch[ch], preout->ch[ch], s->dither.noise.ch[ch] + s->dither.noise.bps * s->dither.noise_pos, s->native_one, 0, 0, out_count);
|
|
}
|
|
} else {
|
|
switch(s->int_sample_fmt) {
|
|
case AV_SAMPLE_FMT_S16P :swri_noise_shaping_int16(s, conv_src, preout, &s->dither.noise, out_count); break;
|
|
case AV_SAMPLE_FMT_S32P :swri_noise_shaping_int32(s, conv_src, preout, &s->dither.noise, out_count); break;
|
|
case AV_SAMPLE_FMT_FLTP :swri_noise_shaping_float(s, conv_src, preout, &s->dither.noise, out_count); break;
|
|
case AV_SAMPLE_FMT_DBLP :swri_noise_shaping_double(s,conv_src, preout, &s->dither.noise, out_count); break;
|
|
}
|
|
}
|
|
s->dither.noise_pos += out_count;
|
|
}
|
|
//FIXME packed doesn't need more than 1 chan here!
|
|
swri_audio_convert(s->out_convert, out, conv_src, out_count);
|
|
}
|
|
return out_count;
|
|
}
|
|
|
|
int swr_is_initialized(struct SwrContext *s) {
|
|
return !!s->in_buffer.ch_count;
|
|
}
|
|
|
|
int attribute_align_arg swr_convert(struct SwrContext *s,
|
|
uint8_t * const *out_arg, int out_count,
|
|
const uint8_t * const *in_arg, int in_count)
|
|
{
|
|
AudioData * in= &s->in;
|
|
AudioData *out= &s->out;
|
|
int av_unused max_output;
|
|
|
|
if (!swr_is_initialized(s)) {
|
|
av_log(s, AV_LOG_ERROR, "Context has not been initialized\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
#if defined(ASSERT_LEVEL) && ASSERT_LEVEL >1
|
|
max_output = swr_get_out_samples(s, in_count);
|
|
#endif
|
|
|
|
while(s->drop_output > 0){
|
|
int ret;
|
|
uint8_t *tmp_arg[SWR_CH_MAX];
|
|
#define MAX_DROP_STEP 16384
|
|
if((ret=swri_realloc_audio(&s->drop_temp, FFMIN(s->drop_output, MAX_DROP_STEP)))<0)
|
|
return ret;
|
|
|
|
reversefill_audiodata(&s->drop_temp, tmp_arg);
|
|
s->drop_output *= -1; //FIXME find a less hackish solution
|
|
ret = swr_convert(s, tmp_arg, FFMIN(-s->drop_output, MAX_DROP_STEP), in_arg, in_count); //FIXME optimize but this is as good as never called so maybe it doesn't matter
|
|
s->drop_output *= -1;
|
|
in_count = 0;
|
|
if(ret>0) {
|
|
s->drop_output -= ret;
|
|
if (!s->drop_output && !out_arg)
|
|
return 0;
|
|
continue;
|
|
}
|
|
|
|
av_assert0(s->drop_output);
|
|
return 0;
|
|
}
|
|
|
|
if(!in_arg){
|
|
if(s->resample){
|
|
if (!s->flushed)
|
|
s->resampler->flush(s);
|
|
s->resample_in_constraint = 0;
|
|
s->flushed = 1;
|
|
}else if(!s->in_buffer_count){
|
|
return 0;
|
|
}
|
|
}else
|
|
fill_audiodata(in , (void*)in_arg);
|
|
|
|
fill_audiodata(out, out_arg);
|
|
|
|
if(s->resample){
|
|
int ret = swr_convert_internal(s, out, out_count, in, in_count);
|
|
if(ret>0 && !s->drop_output)
|
|
s->outpts += ret * (int64_t)s->in_sample_rate;
|
|
|
|
av_assert2(max_output < 0 || ret <= max_output);
|
|
|
|
return ret;
|
|
}else{
|
|
AudioData tmp= *in;
|
|
int ret2=0;
|
|
int ret, size;
|
|
size = FFMIN(out_count, s->in_buffer_count);
|
|
if(size){
|
|
buf_set(&tmp, &s->in_buffer, s->in_buffer_index);
|
|
ret= swr_convert_internal(s, out, size, &tmp, size);
|
|
if(ret<0)
|
|
return ret;
|
|
ret2= ret;
|
|
s->in_buffer_count -= ret;
|
|
s->in_buffer_index += ret;
|
|
buf_set(out, out, ret);
|
|
out_count -= ret;
|
|
if(!s->in_buffer_count)
|
|
s->in_buffer_index = 0;
|
|
}
|
|
|
|
if(in_count){
|
|
size= s->in_buffer_index + s->in_buffer_count + in_count - out_count;
|
|
|
|
if(in_count > out_count) { //FIXME move after swr_convert_internal
|
|
if( size > s->in_buffer.count
|
|
&& s->in_buffer_count + in_count - out_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=swri_realloc_audio(&s->in_buffer, size)) < 0)
|
|
return ret;
|
|
}
|
|
|
|
if(out_count){
|
|
size = FFMIN(in_count, out_count);
|
|
ret= swr_convert_internal(s, out, size, in, size);
|
|
if(ret<0)
|
|
return ret;
|
|
buf_set(in, in, ret);
|
|
in_count -= ret;
|
|
ret2 += ret;
|
|
}
|
|
if(in_count){
|
|
buf_set(&tmp, &s->in_buffer, s->in_buffer_index + s->in_buffer_count);
|
|
copy(&tmp, in, in_count);
|
|
s->in_buffer_count += in_count;
|
|
}
|
|
}
|
|
if(ret2>0 && !s->drop_output)
|
|
s->outpts += ret2 * (int64_t)s->in_sample_rate;
|
|
av_assert2(max_output < 0 || ret2 < 0 || ret2 <= max_output);
|
|
return ret2;
|
|
}
|
|
}
|
|
|
|
int swr_drop_output(struct SwrContext *s, int count){
|
|
const uint8_t *tmp_arg[SWR_CH_MAX];
|
|
s->drop_output += count;
|
|
|
|
if(s->drop_output <= 0)
|
|
return 0;
|
|
|
|
av_log(s, AV_LOG_VERBOSE, "discarding %d audio samples\n", count);
|
|
return swr_convert(s, NULL, s->drop_output, tmp_arg, 0);
|
|
}
|
|
|
|
int swr_inject_silence(struct SwrContext *s, int count){
|
|
int ret, i;
|
|
uint8_t *tmp_arg[SWR_CH_MAX];
|
|
|
|
if(count <= 0)
|
|
return 0;
|
|
|
|
#define MAX_SILENCE_STEP 16384
|
|
while (count > MAX_SILENCE_STEP) {
|
|
if ((ret = swr_inject_silence(s, MAX_SILENCE_STEP)) < 0)
|
|
return ret;
|
|
count -= MAX_SILENCE_STEP;
|
|
}
|
|
|
|
if((ret=swri_realloc_audio(&s->silence, count))<0)
|
|
return ret;
|
|
|
|
if(s->silence.planar) for(i=0; i<s->silence.ch_count; i++) {
|
|
memset(s->silence.ch[i], s->silence.bps==1 ? 0x80 : 0, count*s->silence.bps);
|
|
} else
|
|
memset(s->silence.ch[0], s->silence.bps==1 ? 0x80 : 0, count*s->silence.bps*s->silence.ch_count);
|
|
|
|
reversefill_audiodata(&s->silence, tmp_arg);
|
|
av_log(s, AV_LOG_VERBOSE, "adding %d audio samples of silence\n", count);
|
|
ret = swr_convert(s, NULL, 0, (const uint8_t**)tmp_arg, count);
|
|
return ret;
|
|
}
|
|
|
|
int64_t swr_get_delay(struct SwrContext *s, int64_t base){
|
|
if (s->resampler && s->resample){
|
|
return s->resampler->get_delay(s, base);
|
|
}else{
|
|
return (s->in_buffer_count*base + (s->in_sample_rate>>1))/ s->in_sample_rate;
|
|
}
|
|
}
|
|
|
|
int swr_get_out_samples(struct SwrContext *s, int in_samples)
|
|
{
|
|
int64_t out_samples;
|
|
|
|
if (in_samples < 0)
|
|
return AVERROR(EINVAL);
|
|
|
|
if (s->resampler && s->resample) {
|
|
if (!s->resampler->get_out_samples)
|
|
return AVERROR(ENOSYS);
|
|
out_samples = s->resampler->get_out_samples(s, in_samples);
|
|
} else {
|
|
out_samples = s->in_buffer_count + in_samples;
|
|
av_assert0(s->out_sample_rate == s->in_sample_rate);
|
|
}
|
|
|
|
if (out_samples > INT_MAX)
|
|
return AVERROR(EINVAL);
|
|
|
|
return out_samples;
|
|
}
|
|
|
|
int swr_set_compensation(struct SwrContext *s, int sample_delta, int compensation_distance){
|
|
int ret;
|
|
|
|
if (!s || compensation_distance < 0)
|
|
return AVERROR(EINVAL);
|
|
if (!compensation_distance && sample_delta)
|
|
return AVERROR(EINVAL);
|
|
if (!s->resample) {
|
|
s->flags |= SWR_FLAG_RESAMPLE;
|
|
ret = swr_init(s);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
if (!s->resampler->set_compensation){
|
|
return AVERROR(EINVAL);
|
|
}else{
|
|
return s->resampler->set_compensation(s->resample, sample_delta, compensation_distance);
|
|
}
|
|
}
|
|
|
|
int64_t swr_next_pts(struct SwrContext *s, int64_t pts){
|
|
if(pts == INT64_MIN)
|
|
return s->outpts;
|
|
|
|
if (s->firstpts == AV_NOPTS_VALUE)
|
|
s->outpts = s->firstpts = pts;
|
|
|
|
if(s->min_compensation >= FLT_MAX) {
|
|
return (s->outpts = pts - swr_get_delay(s, s->in_sample_rate * (int64_t)s->out_sample_rate));
|
|
} else {
|
|
int64_t delta = pts - swr_get_delay(s, s->in_sample_rate * (int64_t)s->out_sample_rate) - s->outpts + s->drop_output*(int64_t)s->in_sample_rate;
|
|
double fdelta = delta /(double)(s->in_sample_rate * (int64_t)s->out_sample_rate);
|
|
|
|
if(fabs(fdelta) > s->min_compensation) {
|
|
if(s->outpts == s->firstpts || fabs(fdelta) > s->min_hard_compensation){
|
|
int ret;
|
|
if(delta > 0) ret = swr_inject_silence(s, delta / s->out_sample_rate);
|
|
else ret = swr_drop_output (s, -delta / s-> in_sample_rate);
|
|
if(ret<0){
|
|
av_log(s, AV_LOG_ERROR, "Failed to compensate for timestamp delta of %f\n", fdelta);
|
|
}
|
|
} else if(s->soft_compensation_duration && s->max_soft_compensation) {
|
|
int duration = s->out_sample_rate * s->soft_compensation_duration;
|
|
double max_soft_compensation = s->max_soft_compensation / (s->max_soft_compensation < 0 ? -s->in_sample_rate : 1);
|
|
int comp = av_clipf(fdelta, -max_soft_compensation, max_soft_compensation) * duration ;
|
|
av_log(s, AV_LOG_VERBOSE, "compensating audio timestamp drift:%f compensation:%d in:%d\n", fdelta, comp, duration);
|
|
swr_set_compensation(s, comp, duration);
|
|
}
|
|
}
|
|
|
|
return s->outpts;
|
|
}
|
|
}
|