external_ffmpeg/libavfilter/volume.c

461 lines
15 KiB
C

/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio volume for src filter
*/
#include "libavutil/mem.h"
#include "libavutil/samplefmt.h"
#include <math.h>
#include <limits.h>
#include "volume.h"
#define CONVERT_PACKED(src_type, dst_type, src_enum, dst_enum, convert_expr) \
case src_enum: \
if (dst_fmt == dst_enum) { \
const src_type *s = (const src_type *)src; \
dst_type *d = (dst_type *)dst; \
int total = nb_samples * chs; \
for (int i = 0; i < total; i++) d[i] = convert_expr; \
return 0; \
} \
break
#define CONVERT_PLANAR(src_type, dst_type, src_enum, dst_enum, convert_expr) \
case src_enum: \
if (dst_fmt == dst_enum) { \
const src_type *const *s = (const src_type *const *)src; \
dst_type **d = (dst_type **)dst; \
for (int c = 0; c < chs; c++) { \
for (int i = 0; i < nb_samples; i++) d[c][i] = convert_expr; \
} \
return 0; \
} \
break
static int bit_convert(enum AVSampleFormat src_fmt, enum AVSampleFormat dst_fmt,
const void *src, void *dst, int nb_samples, int chs) {
/* Check integer multiplication overflow */
if ((long long)nb_samples * chs > INT_MAX) {
av_log(NULL, AV_LOG_ERROR, "Total samples overflow\n");
return AVERROR(ERANGE);
}
switch (src_fmt) {
CONVERT_PACKED(uint8_t, float, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_FLT, (s[i] - 128.0f) / 128.0f);
CONVERT_PLANAR(uint8_t, float, AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_FLTP, (s[c][i] - 128.0f) / 128.0f);
CONVERT_PACKED(int16_t, float, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT, s[i] / 32767.0f);
CONVERT_PLANAR(int16_t, float, AV_SAMPLE_FMT_S16P,AV_SAMPLE_FMT_FLTP, s[c][i] / 32767.0f);
CONVERT_PACKED(int32_t, float, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT, s[i] / 2147483647.0f);
CONVERT_PLANAR(int32_t, float, AV_SAMPLE_FMT_S32P,AV_SAMPLE_FMT_FLTP, s[c][i] / 2147483647.0f);
default: break;
}
switch (src_fmt) {
CONVERT_PACKED(float, int16_t, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16, av_clip_int16(lrintf(s[i] * 32767.0f)));
CONVERT_PLANAR(float, int16_t, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16P, av_clip_int16(lrintf(s[c][i] * 32767.0f)));
default: break;
}
av_log(NULL, AV_LOG_ERROR, "Unsupported convert: %s → %s\n",
av_get_sample_fmt_name(src_fmt), av_get_sample_fmt_name(dst_fmt));
return AVERROR(EINVAL);
}
static inline void fade_samples_u8_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int chs, int16_t dst_volume, int16_t src_volume)
{
int i, j, k = 0;
int32_t step;
step = ((int32_t)(dst_volume - src_volume) * (1 << 15)) / nb_samples;
for (i = 0; i < nb_samples; i++) {
for (j = 0; j < chs; j++, k++) {
dst[k] = av_clip_uint8(((src[k] * (src_volume + (step * i >> 15))) >> 8));
}
}
}
static inline void fade_samples_s16_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int chs, int16_t dst_volume, int16_t src_volume)
{
int i, j, k = 0;
int32_t step;
int16_t *smp_dst = (int16_t *)dst;
const int16_t *smp_src = (const int16_t *)src;
step = ((int32_t)(dst_volume - src_volume) * (1 << 15)) / nb_samples;
for (i = 0; i < nb_samples; i++) {
for (j = 0; j < chs; j++, k++) {
smp_dst[k] = av_clip_int16((smp_src[k] * (src_volume + (step * i >> 15))) >> 8);
}
}
}
static inline void fade_samples_s32_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int chs, int16_t dst_volume, int16_t src_volume)
{
int i, j, k = 0;
int64_t step;
int32_t *smp_dst = (int32_t *)dst;
const int32_t *smp_src = (const int32_t *)src;
step = ((int64_t)(dst_volume - src_volume) * (1LL << 31)) / nb_samples;
for (i = 0; i < nb_samples; i++) {
for (j = 0; j < chs; j++, k++) {
smp_dst[k] = av_clipl_int32(((int64_t)smp_src[k] * (src_volume + (step * i >> 31))) >> 8);
}
}
}
static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
for (i = 0; i < nb_samples; i++)
dst[i] = av_clip_uint8(((((int64_t)src[i] - 128) * volume + 128) >> 8) + 128);
}
static inline void scale_samples_u8_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
for (i = 0; i < nb_samples; i++)
dst[i] = av_clip_uint8((((src[i] - 128) * volume + 128) >> 8) + 128);
}
static inline void scale_samples_s16(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
int16_t *smp_dst = (int16_t *)dst;
const int16_t *smp_src = (const int16_t *)src;
for (i = 0; i < nb_samples; i++)
smp_dst[i] = av_clip_int16(((int64_t)smp_src[i] * volume + 128) >> 8);
}
static inline void scale_samples_s16_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
int16_t *smp_dst = (int16_t *)dst;
const int16_t *smp_src = (const int16_t *)src;
for (i = 0; i < nb_samples; i++)
smp_dst[i] = av_clip_int16((smp_src[i] * volume + 128) >> 8);
}
static inline void scale_samples_s32(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
int32_t *smp_dst = (int32_t *)dst;
const int32_t *smp_src = (const int32_t *)src;
for (i = 0; i < nb_samples; i++)
smp_dst[i] = av_clipl_int32((((int64_t)smp_src[i] * volume + 128) >> 8));
}
static av_cold void scaler_init(VolumeContext *vol)
{
/* Use standard rounding function */
int32_t volume_i = (int32_t)lround(vol->volume * 256);
vol->samples_align = 1;
/* use the processing format (mid_fmt) so pointers stay valid after down-convert */
switch (av_get_packed_sample_fmt(vol->mid_fmt)) {
case AV_SAMPLE_FMT_U8:
if (volume_i < 0x1000000)
vol->scale_samples = scale_samples_u8_small;
else
vol->scale_samples = scale_samples_u8;
break;
case AV_SAMPLE_FMT_S16:
if (volume_i < 0x10000)
vol->scale_samples = scale_samples_s16_small;
else
vol->scale_samples = scale_samples_s16;
break;
case AV_SAMPLE_FMT_S32:
vol->scale_samples = scale_samples_s32;
break;
case AV_SAMPLE_FMT_FLT:
vol->samples_align = 4;
break;
case AV_SAMPLE_FMT_DBL:
vol->samples_align = 8;
break;
}
}
static av_cold void fader_init(VolumeContext *vol)
{
/* Keep fade function pointers in sync with the processing format. */
switch (av_get_packed_sample_fmt(vol->mid_fmt)) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
vol->fade_samples = fade_samples_u8_small;
break;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
vol->fade_samples = fade_samples_s16_small;
break;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
vol->fade_samples = fade_samples_s32_small;
break;
default:
vol->fade_samples = NULL;
break;
}
}
static bool is_fixed(enum AVSampleFormat fmt)
{
switch (fmt) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
return true;
default:
return false;
}
}
static bool is_float(enum AVSampleFormat fmt)
{
switch (fmt) {
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
return true;
default:
return false;
}
}
static enum AVSampleFormat pick_mid_fmt(enum AVSampleFormat src, enum PrecisionType precision)
{
int planar = av_sample_fmt_is_planar(src);
if (precision == PRECISION_FLOAT && (
src == AV_SAMPLE_FMT_U8 || src == AV_SAMPLE_FMT_U8P ||
src == AV_SAMPLE_FMT_S16 || src == AV_SAMPLE_FMT_S16P ||
src == AV_SAMPLE_FMT_S32 || src == AV_SAMPLE_FMT_S32P))
return planar ? AV_SAMPLE_FMT_FLTP : AV_SAMPLE_FMT_FLT;
if (precision == PRECISION_FIXED && (
src == AV_SAMPLE_FMT_FLT || src == AV_SAMPLE_FMT_FLTP))
return planar ? AV_SAMPLE_FMT_S16P : AV_SAMPLE_FMT_S16;
return src;
}
static AVFrame *get_conv_frame(VolumeContext *vol, AVFrame *src, enum AVSampleFormat fmt)
{
int ret;
if (!vol->conv_frame)
return NULL;
if (vol->conv_frame->format != fmt ||
vol->conv_frame->nb_samples != src->nb_samples) {
av_frame_unref(vol->conv_frame);
vol->conv_frame->format = fmt;
vol->conv_frame->nb_samples = src->nb_samples;
vol->conv_frame->sample_rate = src->sample_rate;
vol->conv_frame->ch_layout = src->ch_layout;
ret = av_frame_get_buffer(vol->conv_frame, 0);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to get conv frame buffer\n");
return NULL;
}
}
return vol->conv_frame;
}
void volume_set(VolumeContext *vol, double volume)
{
/* Should not fade in first frame, cause there is no src volume. */
vol->volume_last = vol->volume_last > 0 ? vol->volume : volume;
vol->volume = volume;
scaler_init(vol);
fader_init(vol);
}
void volume_scale(VolumeContext *vol, AVFrame *frame)
{
int planar, planes, plane_size, p, need_convert, ret;
AVFrame *proc_frame = frame;
if (!vol || !frame)
return;
planar = av_sample_fmt_is_planar(frame->format);
planes = planar ? frame->ch_layout.nb_channels : 1;
plane_size = frame->nb_samples * (planar ? 1 : frame->ch_layout.nb_channels);
need_convert = (vol->mid_fmt != frame->format);
if (vol->volume_last < 0)
vol->volume_last = vol->volume; /* If volume not set after init, skip fade in first frame. */
/* convert to intermediate format if needed */
if (need_convert) {
proc_frame = get_conv_frame(vol, frame, vol->mid_fmt);
if (!proc_frame) {
av_log(NULL, AV_LOG_ERROR, "get_conv_frame failed\n");
return;
}
ret = bit_convert(frame->format, vol->mid_fmt,
planar ? (const void *)frame->extended_data : frame->data[0],
av_sample_fmt_is_planar(vol->mid_fmt) ? (void *)proc_frame->extended_data : proc_frame->data[0],
frame->nb_samples, frame->ch_layout.nb_channels);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Convert to mid format failed\n");
return;
}
}
/* apply volume scaling / fading */
if (is_fixed(proc_frame->format)) {
int32_t vol_isrc = (int32_t)lround(vol->volume_last * 256);
int32_t volume_i = (int32_t)lround(vol->volume * 256);
if (volume_i != vol_isrc) {
for (p = 0; p < planes; p++) {
vol->fade_samples(proc_frame->extended_data[p],
proc_frame->extended_data[p],
proc_frame->nb_samples, planar ? 1 : proc_frame->ch_layout.nb_channels,
volume_i, vol_isrc);
}
} else {
for (p = 0; p < planes; p++) {
vol->scale_samples(proc_frame->extended_data[p],
proc_frame->extended_data[p],
plane_size, volume_i);
}
}
vol->volume_last = vol->volume;
} else if (is_float(proc_frame->format)) {
for (p = 0; p < planes; p++) {
vol->fdsp->vector_fmul_scalar((float *)proc_frame->extended_data[p],
(float *)proc_frame->extended_data[p],
vol->volume, plane_size);
}
} else {
for (p = 0; p < planes; p++) {
vol->fdsp->vector_dmul_scalar((double *)proc_frame->extended_data[p],
(double *)proc_frame->extended_data[p],
vol->volume, plane_size);
}
}
/* convert back to original format if needed */
if (need_convert) {
ret = bit_convert(vol->mid_fmt, frame->format,
av_sample_fmt_is_planar(vol->mid_fmt) ? (const void *)proc_frame->extended_data : proc_frame->data[0],
planar ? (void *)frame->extended_data : frame->data[0],
frame->nb_samples, frame->ch_layout.nb_channels);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Convert to original format failed\n");
return;
}
}
}
int volume_init(VolumeContext *vol, enum AVSampleFormat sample_fmt, enum PrecisionType precision)
{
if (!vol) {
av_log(NULL, AV_LOG_ERROR, "VolumeContext is NULL\n");
return AVERROR(EINVAL);
}
vol->sample_fmt = sample_fmt;
vol->precision = precision;
vol->volume_last = -1.0f;
vol->volume = 1.0f;
av_frame_free(&vol->conv_frame);
vol->conv_frame = av_frame_alloc();
if (!vol->conv_frame)
return AVERROR(ENOMEM);
vol->mid_fmt = pick_mid_fmt(sample_fmt, precision);
if (vol->fdsp)
av_freep(&vol->fdsp);
vol->fdsp = avpriv_float_dsp_alloc(0);
if (!vol->fdsp) {
av_frame_free(&vol->conv_frame);
return AVERROR(ENOMEM);
}
scaler_init(vol);
fader_init(vol);
return 0;
}
void volume_uninit(VolumeContext *vol)
{
av_freep(&vol->fdsp);
if (vol->conv_frame)
av_frame_free(&vol->conv_frame);
}
int volume_parse_index_db(const char *str, int *index, double *value)
{
const char *p;
char *end;
long idx;
int ret;
if (!str || !index || !value)
return AVERROR(EINVAL);
p = str;
while (av_isspace(*p))
p++;
*index = -1;
idx = strtol(p, &end, 0);
if (end != p && av_isspace(*end)) {
if (idx < -1 || idx > INT_MAX)
return AVERROR(EINVAL);
*index = (int)idx;
p = end;
while (av_isspace(*p))
p++;
}
ret = av_expr_parse_and_eval(value, p,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, 0, NULL);
return ret;
}