mirror of
https://github.com/xenia-project/FFmpeg.git
synced 2024-11-27 13:30:45 +00:00
Merge remote-tracking branch 'qatar/master'
* qatar/master: FATE: use updated reference for aac-latm_stereo_to_51 avconv: use libavresample Add libavresample FATE: avoid channel mixing in lavf-dv_fmt Conflicts: Changelog Makefile cmdutils.c configure doc/APIchanges ffmpeg.c tests/lavf-regression.sh tests/ref/lavf/dv_fmt tests/ref/seek/lavf_dv Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
commit
3ead79eaa3
@ -26,6 +26,7 @@ version next:
|
||||
- drawtext video filter: fontconfig support
|
||||
- ffmpeg -benchmark_all option
|
||||
- super2xsai filter ported from libmpcodecs
|
||||
- add libavresample audio conversion library for compatibility
|
||||
|
||||
|
||||
version 0.10:
|
||||
|
1
Makefile
1
Makefile
@ -31,6 +31,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_POSTPROC) += postproc
|
||||
FFLIBS-$(CONFIG_SWRESAMPLE)+= swresample
|
||||
|
@ -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 "libswresample/swresample.h"
|
||||
#if CONFIG_POSTPROC
|
||||
@ -633,7 +634,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%-11s %2d.%3d.%3d / %2d.%3d.%3d\n",\
|
||||
av_log(NULL, level, \
|
||||
"%slib%-11s %2d.%3d.%3d / %2d.%3d.%3d\n", \
|
||||
indent, #libname, \
|
||||
LIB##LIBNAME##_VERSION_MAJOR, \
|
||||
LIB##LIBNAME##_VERSION_MINOR, \
|
||||
@ -662,6 +664,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);
|
||||
PRINT_LIB_INFO(swresample,SWRESAMPLE, flags, level);
|
||||
#if CONFIG_POSTPROC
|
||||
|
@ -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 swresample
|
||||
ALLFFLIBS = avcodec avdevice avfilter avformat avresample avutil postproc swscale swresample
|
||||
|
||||
# NASM requires -I path terminated with /
|
||||
IFLAGS := -I. -I$(SRC_PATH)/
|
||||
|
5
configure
vendored
5
configure
vendored
@ -112,6 +112,7 @@ Component options:
|
||||
--disable-swscale disable libswscale build
|
||||
--disable-postproc disable libpostproc 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]
|
||||
--disable-os2threads disable OS/2 threads [auto]
|
||||
@ -1013,6 +1014,7 @@ CONFIG_LIST="
|
||||
avdevice
|
||||
avfilter
|
||||
avformat
|
||||
avresample
|
||||
avisynth
|
||||
bzlib
|
||||
crystalhd
|
||||
@ -1870,6 +1872,7 @@ enable avcodec
|
||||
enable avdevice
|
||||
enable avfilter
|
||||
enable avformat
|
||||
enable avresample
|
||||
enable avutil
|
||||
enable postproc
|
||||
enable stripping
|
||||
@ -3724,6 +3727,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 LIBPOSTPROC libpostproc/postprocess.h
|
||||
get_version LIBSWRESAMPLE libswresample/swresample.h
|
||||
@ -3869,5 +3873,6 @@ pkgconfig_generate libavformat "FFmpeg container format library" "$LIBAVFORMAT_V
|
||||
pkgconfig_generate libavdevice "FFmpeg device handling library" "$LIBAVDEVICE_VERSION" "$extralibs" "$libavdevice_pc_deps"
|
||||
pkgconfig_generate libavfilter "FFmpeg video filtering library" "$LIBAVFILTER_VERSION" "$extralibs" "$libavfilter_pc_deps"
|
||||
pkgconfig_generate libpostproc "FFmpeg postprocessing library" "$LIBPOSTPROC_VERSION" "" "libavutil = $LIBAVUTIL_VERSION"
|
||||
pkgconfig_generate libavresample "Libav audio resampling library" "$LIBAVRESAMPLE_VERSION" "$extralibs"
|
||||
pkgconfig_generate libswscale "FFmpeg image rescaling library" "$LIBSWSCALE_VERSION" "$LIBM" "libavutil = $LIBAVUTIL_VERSION"
|
||||
pkgconfig_generate libswresample "FFmpeg audio rescaling library" "$LIBSWRESAMPLE_VERSION" "$LIBM" "libavutil = $LIBAVUTIL_VERSION"
|
||||
|
@ -6,6 +6,7 @@ libavcodec: 2012-01-27
|
||||
libavdevice: 2011-04-18
|
||||
libavfilter: 2011-04-18
|
||||
libavformat: 2012-01-27
|
||||
libavresample: 2012-xx-xx
|
||||
libpostproc: 2011-04-18
|
||||
libswscale: 2011-06-20
|
||||
libavutil: 2011-04-18
|
||||
@ -22,6 +23,9 @@ API changes, most recent first:
|
||||
2012-03-26 - a67d9cf - lavfi 2.66.100
|
||||
Add avfilter_fill_frame_from_{audio_,}buffer_ref() functions.
|
||||
|
||||
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()
|
||||
|
55
ffmpeg.c
55
ffmpeg.c
@ -36,7 +36,6 @@
|
||||
#include "libavdevice/avdevice.h"
|
||||
#include "libswscale/swscale.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "libavcodec/audioconvert.h"
|
||||
#include "libavutil/audioconvert.h"
|
||||
#include "libavutil/parseutils.h"
|
||||
#include "libavutil/samplefmt.h"
|
||||
@ -300,6 +299,7 @@ typedef struct OutputStream {
|
||||
int audio_channels_mapped; ///< number of channels in audio_channels_map
|
||||
int resample_sample_fmt;
|
||||
int resample_channels;
|
||||
uint64_t resample_channel_layout;
|
||||
int resample_sample_rate;
|
||||
float rematrix_volume;
|
||||
AVFifoBuffer *fifo; /* for compression: one audio fifo per codec */
|
||||
@ -1525,7 +1525,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;
|
||||
@ -1538,7 +1538,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)
|
||||
@ -1557,7 +1557,7 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
|
||||
uint8_t *buftmp;
|
||||
int64_t size_out;
|
||||
|
||||
int frame_bytes, resample_changed;
|
||||
int 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);
|
||||
@ -1566,37 +1566,46 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
|
||||
int size = decoded_frame->nb_samples * dec->channels * isize;
|
||||
int planes = av_sample_fmt_is_planar(dec->sample_fmt) ? dec->channels : 1;
|
||||
int i;
|
||||
int out_linesize = 0;
|
||||
int buf_linesize = decoded_frame->linesize[0];
|
||||
|
||||
av_assert0(planes <= AV_NUM_DATA_POINTERS);
|
||||
|
||||
for(i=0; i<planes; i++)
|
||||
buf[i]= decoded_frame->data[i];
|
||||
|
||||
|
||||
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_fmt != dec->sample_fmt
|
||||
|| 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->swr) || resample_changed || ost->audio_channels_mapped) {
|
||||
|
||||
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;
|
||||
swr_free(&ost->swr);
|
||||
}
|
||||
@ -1604,6 +1613,7 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
|
||||
if (audio_sync_method <= 1 && !ost->audio_channels_mapped &&
|
||||
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->swr = NULL;
|
||||
ost->audio_resample = 0;
|
||||
@ -1673,7 +1683,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);
|
||||
}
|
||||
@ -1686,11 +1696,11 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
|
||||
buf[i] = t;
|
||||
}
|
||||
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));
|
||||
@ -1703,8 +1713,10 @@ static void do_audio_out(AVFormatContext *s, OutputStream *ost,
|
||||
|
||||
if (ost->audio_resample || ost->audio_channels_mapped) {
|
||||
buftmp = audio_buf;
|
||||
size_out = swr_convert(ost->swr, ( uint8_t*[]){buftmp}, allocated_audio_buf_size / (enc->channels * osize),
|
||||
buf, size / (dec->channels * isize));
|
||||
size_out = swr_convert(ost->swr, ( uint8_t*[]){buftmp},
|
||||
allocated_audio_buf_size / (enc->channels * osize),
|
||||
buf,
|
||||
size / (dec->channels * isize));
|
||||
if (size_out < 0) {
|
||||
av_log(NULL, AV_LOG_FATAL, "swr_convert failed\n");
|
||||
exit_program(1);
|
||||
@ -3078,6 +3090,7 @@ static int transcode_init(void)
|
||||
if (!ost->fifo) {
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
if (!codec->sample_rate)
|
||||
codec->sample_rate = icodec->sample_rate;
|
||||
choose_sample_rate(ost->st, ost->enc);
|
||||
@ -3110,13 +3123,15 @@ static int transcode_init(void)
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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) {
|
||||
|
15
libavresample/Makefile
Normal file
15
libavresample/Makefile
Normal file
@ -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
|
334
libavresample/audio_convert.c
Normal file
334
libavresample/audio_convert.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
87
libavresample/audio_convert.h
Normal file
87
libavresample/audio_convert.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 */
|
345
libavresample/audio_data.c
Normal file
345
libavresample/audio_data.c
Normal file
@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
173
libavresample/audio_data.h
Normal file
173
libavresample/audio_data.h
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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 */
|
356
libavresample/audio_mix.c
Normal file
356
libavresample/audio_mix.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
108
libavresample/audio_mix.h
Normal file
108
libavresample/audio_mix.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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 */
|
346
libavresample/audio_mix_matrix.c
Normal file
346
libavresample/audio_mix_matrix.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
340
libavresample/avresample-test.c
Normal file
340
libavresample/avresample-test.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2002 Fabrice Bellard
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 [<num formats> "
|
||||
"[<num sample rates> [<num channel layouts>]]]\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;
|
||||
}
|
283
libavresample/avresample.h
Normal file
283
libavresample/avresample.h
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 */
|
75
libavresample/internal.h
Normal file
75
libavresample/internal.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 */
|
4
libavresample/libavresample.v
Normal file
4
libavresample/libavresample.v
Normal file
@ -0,0 +1,4 @@
|
||||
LIBAVRESAMPLE_$MAJOR {
|
||||
global: av*;
|
||||
local: *;
|
||||
};
|
89
libavresample/options.c
Normal file
89
libavresample/options.c
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
480
libavresample/resample.c
Normal file
480
libavresample/resample.c
Normal file
@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
70
libavresample/resample.h
Normal file
70
libavresample/resample.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
|
||||
*
|
||||
* 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 */
|
405
libavresample/utils.c
Normal file
405
libavresample/utils.c
Normal file
@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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 FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1;
|
||||
}
|
||||
|
||||
const char *avresample_configuration(void)
|
||||
{
|
||||
return FFMPEG_CONFIGURATION;
|
||||
}
|
41
libavresample/version.h
Normal file
41
libavresample/version.h
Normal file
@ -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 */
|
5
libavresample/x86/Makefile
Normal file
5
libavresample/x86/Makefile
Normal file
@ -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
|
104
libavresample/x86/audio_convert.asm
Normal file
104
libavresample/x86/audio_convert.asm
Normal file
@ -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
|
42
libavresample/x86/audio_convert_init.c
Normal file
42
libavresample/x86/audio_convert_init.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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
|
||||
}
|
64
libavresample/x86/audio_mix.asm
Normal file
64
libavresample/x86/audio_mix.asm
Normal file
@ -0,0 +1,64 @@
|
||||
;******************************************************************************
|
||||
;* x86 optimized channel mixing
|
||||
;* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
;*
|
||||
;* 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
|
44
libavresample/x86/audio_mix_init.c
Normal file
44
libavresample/x86/audio_mix_init.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
|
||||
*
|
||||
* 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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -118,7 +118,7 @@ fi
|
||||
if [ -n "$do_dv_fmt" ] ; then
|
||||
do_lavf_timecode_nodrop dv "-ar 48000 -r 25 -s pal -ac 2"
|
||||
do_lavf_timecode_drop dv "-ar 48000 -pix_fmt yuv411p -s ntsc -ac 2"
|
||||
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
|
||||
|
@ -4,6 +4,6 @@
|
||||
cc33ae4f9e6828914dea0f09d1241b7e *./tests/data/lavf/lavf.dv
|
||||
3480000 ./tests/data/lavf/lavf.dv
|
||||
./tests/data/lavf/lavf.dv CRC=0x8d5e9e8f
|
||||
b36c83cd0ba0ebe719f09f885c4bbcd3 *./tests/data/lavf/lavf.dv
|
||||
87d3b20f656235671383a7eaa2f66330 *./tests/data/lavf/lavf.dv
|
||||
3600000 ./tests/data/lavf/lavf.dv
|
||||
./tests/data/lavf/lavf.dv CRC=0x2bc2ae3a
|
||||
./tests/data/lavf/lavf.dv CRC=0x0e868a82
|
||||
|
Loading…
Reference in New Issue
Block a user