1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-28 10:02:17 +00:00

audio/filter: split af_format into separate filters, rename af_force

af_format is the old audio conversion filter. It could do all possible
conversions supported by the audio chain. However, ever since the
addition of af_lavrresample, most conversions are done by
libav/swresample, and af_format is used as fallback.

Separate out the fallback cases and remove af_format. af_convert24 does
24 bit <-> 32 bit conversions, while af_convertsignendian does sign and
endian conversions. Maybe the way the conversions are split sounds a bit
odd. But the former changes the size of the audio data, while the latter
is fully in-place, so there's at least different buffer management.

This requires a quite complicated algorithm to make sure all these
"partial" conversion filters can actually get from one format to
another. E.g. s24le->s32be always requires convertsignendian and
convert24, but af.c has no idea what the intermediate format should
be. So I added a graph search (trying every possible format and
filter) to determine required format and filter. When I wrote this,
it seemed this was still better than messing everything into
af_lavrresample, but maybe this is overkill and I'll change my
opinion. For now, it seems nice to get rid of af_format though.

The AC3->IEC61937 conversion isn't supported anymore, but I don't think
this is needed anywhere. Most AOs test all formats explicitly, or use
the AF_FORMAT_IS_IEC61937() macro (which includes AC3).

One positive consequence of this change is that conversions always
include dithering (done by libav/swresample), instead of possibly going
through af_format, which doesn't do anything fancy.

Rename af_force to af_format. It's essentially compatible with command
line uses of af_format. We retain a compatibility alias for af_force.
This commit is contained in:
wm4 2013-10-22 01:20:43 +02:00
parent 33707c6d63
commit e60b8f181d
8 changed files with 395 additions and 536 deletions

View File

@ -208,7 +208,7 @@ Available filters are:
copy channel 0 to channels 0 to 3. Channel 4 and 5 will contain
silence.
``force=in-format:in-srate:in-channels:out-format:out-srate:out-channels``
``format=format:srate:channels:out-format:out-srate:out-channels``
Force a specific audio format/configuration without actually changing the
audio data. Keep in mind that the filter system might auto-insert actual
conversion filters before or after this filter if needed.
@ -218,15 +218,22 @@ Available filters are:
actually doing a conversion. The data will be 'reinterpreted' by the
filters or audio outputs following this filter.
``<in-format>``
Force conversion to this format. See ``format`` filter for valid audio
format values.
``<format>``
Force conversion to this format. Use ``--af=format=format=help`` to get
a list of valid formats. The general form is 'sbe', where 's' denotes
the sign (either 's' for signed or 'u' for unsigned), 'b' denotes the
number of bits per sample (16, 24 or 32) and 'e' denotes the
endianness ('le' means little-endian, 'be' big-endian and 'ne' the
endianness of the computer mpv is running on). Valid values (amongst
others) are: 's16le', 'u32be' and 'u24ne'. Exceptions to this rule that
are also valid format specifiers: u8, s8, floatle, floatbe, floatne,
mpeg2, and ac3.
``<in-srate>``
``<srate>``
Force conversion to a specific sample rate. The rate is an integer,
48000 for example.
``<in-channels>``
``<channels>``
Force mixing to a specific channel layout. See ``--channels`` option
for possible values.
@ -236,19 +243,21 @@ Available filters are:
``<out-channels>``
``format[=format]``
Convert between different sample formats. Automatically enabled when
needed by the audio output or another filter. See also ``--format``.
See also ``--format``, ``--srate``, and ``--channels`` for related options.
Keep in mind that ``--channels`` does not actually force the number of
channels in most cases, while this filter can do this.
``<format>``
Sets the desired format. The general form is 'sbe', where 's' denotes
the sign (either 's' for signed or 'u' for unsigned), 'b' denotes the
number of bits per sample (16, 24 or 32) and 'e' denotes the
endianness ('le' means little-endian, 'be' big-endian and 'ne' the
endianness of the computer mpv is running on). Valid values (amongst
others) are: 's16le', 'u32be' and 'u24ne'. Exceptions to this rule that
are also valid format specifiers: u8, s8, floatle, floatbe, floatne,
mpeg2, and ac3.
*NOTE*: this filter used to be named ``force``. Also, unlike the old
``format`` filter, this does not do any actual conversion anymore.
Conversion is done by other, automatically inserted filters.
``convert24``
Filter for internal use only. Converts between 24-bit and 32-bit sample
formats.
``convertsignendian``
Filter for internal use only. Converts between signed/unsigned formats
and formats with different endian.
``volume[=v[:sc[:fast]]]``
Implements software volume control. Use this filter with caution since it

View File

@ -150,12 +150,13 @@ SOURCES = audio/audio.c \
audio/filter/af.c \
audio/filter/af_center.c \
audio/filter/af_channels.c \
audio/filter/af_convert24.c \
audio/filter/af_convertsignendian.c \
audio/filter/af_delay.c \
audio/filter/af_dummy.c \
audio/filter/af_equalizer.c \
audio/filter/af_extrastereo.c \
audio/filter/af_force.c \
audio/filter/af_format.c \
audio/filter/af_hrtf.c \
audio/filter/af_karaoke.c \
audio/filter/af_lavcac3enc.c \

View File

@ -52,12 +52,14 @@ extern struct af_info af_info_karaoke;
extern struct af_info af_info_scaletempo;
extern struct af_info af_info_bs2b;
extern struct af_info af_info_lavfi;
extern struct af_info af_info_convert24;
extern struct af_info af_info_convertsignendian;
static struct af_info* filter_list[] = {
&af_info_dummy,
&af_info_delay,
&af_info_channels,
&af_info_force,
&af_info_format,
&af_info_volume,
&af_info_equalizer,
&af_info_pan,
@ -85,8 +87,9 @@ static struct af_info* filter_list[] = {
#ifdef CONFIG_AF_LAVFI
&af_info_lavfi,
#endif
// Must come last, because it's the fallback format conversion filter
&af_info_format,
// Must come last, because they're fallback format conversion filter
&af_info_convert24,
&af_info_convertsignendian,
NULL
};
@ -109,6 +112,10 @@ static bool get_desc(struct m_obj_desc *dst, int index)
const struct m_obj_list af_obj_list = {
.get_desc = get_desc,
.description = "audio filters",
.aliases = {
{"force", "format"},
{0}
},
.legacy_hacks = true, // many filters have custom option parsing
};
@ -285,7 +292,8 @@ static void af_remove(struct af_stream *s, struct af_instance *af)
af->prev->next = af->next;
af->next->prev = af->prev;
af->uninit(af);
if (af->uninit)
af->uninit(af);
talloc_free(af);
}
@ -334,14 +342,76 @@ static int af_count_filters(struct af_stream *s)
return count;
}
static char *af_find_conversion_filter(int srcfmt, int dstfmt)
// Finds the first conversion filter on the way from srcfmt to dstfmt.
// Conversions form a DAG: each node is a format/filter pair, and possible
// conversions are edges. We search the DAG for the shortest path.
// Some cases visit the same filter multiple times, but with different formats
// (like u24le->s8), so one node per format or filter separate is not enough.
// Returns the filter and dest. format for the first conversion step.
// (So we know what conversion filter with what format to insert next.)
static char *af_find_conversion_filter(int srcfmt, int *dstfmt)
{
for (int n = 0; filter_list[n]; n++) {
struct af_info *af = filter_list[n];
if (af->test_conversion && af->test_conversion(srcfmt, dstfmt))
return (char *)af->name;
#define NUM_FMT 64
#define NUM_FILT 32
#define NUM_NODES (NUM_FMT * NUM_FILT)
for (int n = 0; filter_list[n]; n++)
assert(n < NUM_FILT);
for (int n = 0; af_fmtstr_table[n].format; n++)
assert(n < NUM_FMT);
bool visited[NUM_NODES] = {0};
unsigned char distance[NUM_NODES];
short previous[NUM_NODES] = {0};
for (int n = 0; n < NUM_NODES; n++) {
distance[n] = 255;
if (af_fmtstr_table[n % NUM_FMT].format == srcfmt)
distance[n] = 0;
}
return NULL;
while (1) {
int next = -1;
for (int n = 0; n < NUM_NODES; n++) {
if (!visited[n] && (next < 0 || (distance[n] < distance[next])))
next = n;
}
if (next < 0 || distance[next] == 255)
return NULL;
visited[next] = true;
int fmt = next % NUM_FMT;
if (af_fmtstr_table[fmt].format == *dstfmt) {
// Best match found
for (int cur = next; cur >= 0; cur = previous[cur] - 1) {
if (distance[cur] == 1) {
*dstfmt = af_fmtstr_table[cur % NUM_FMT].format;
return (char *)filter_list[cur / NUM_FMT]->name;
}
}
return NULL;
}
for (int n = 0; filter_list[n]; n++) {
struct af_info *af = filter_list[n];
if (!af->test_conversion)
continue;
for (int i = 0; af_fmtstr_table[i].format; i++) {
if (i != fmt && af->test_conversion(af_fmtstr_table[fmt].format,
af_fmtstr_table[i].format))
{
int other = n * NUM_FMT + i;
int ndist = distance[next] + 1;
if (ndist < distance[other]) {
distance[other] = ndist;
previous[other] = next + 1;
}
}
}
}
}
assert(0);
#undef NUM_FMT
#undef NUM_FILT
#undef NODE_N
}
static bool af_is_conversion_filter(struct af_instance *af)
@ -352,7 +422,7 @@ static bool af_is_conversion_filter(struct af_instance *af)
// in is what af can take as input - insert a conversion filter if the actual
// input format doesn't match what af expects.
// Returns:
// AF_OK: must call af_reinit() or equivalent, format matches
// AF_OK: must call af_reinit() or equivalent, format matches (or is closer)
// AF_FALSE: nothing was changed, format matches
// else: error
static int af_fix_format_conversion(struct af_stream *s,
@ -365,18 +435,21 @@ static int af_fix_format_conversion(struct af_stream *s,
struct mp_audio actual = *prev->data;
if (actual.format == in.format)
return AF_FALSE;
if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &in.format) == AF_OK) {
*p_af = prev;
return AF_OK;
}
char *filter = af_find_conversion_filter(actual.format, in.format);
int dstfmt = in.format;
char *filter = af_find_conversion_filter(actual.format, &dstfmt);
if (!filter)
return AF_ERROR;
if (strcmp(filter, prev->info->name) == 0) {
if (prev->control(prev, AF_CONTROL_FORMAT_FMT, &dstfmt) == AF_OK) {
*p_af = prev;
return AF_OK;
}
}
struct af_instance *new = af_prepend(s, af, filter, NULL);
if (new == NULL)
return AF_ERROR;
new->auto_inserted = true;
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &dstfmt)))
return rv;
*p_af = new;
return AF_OK;
@ -442,7 +515,8 @@ static int af_reinit(struct af_stream *s)
// Start with the second filter, as the first filter is the special input
// filter which needs no initialization.
struct af_instance *af = s->first->next;
int max_retry = af_count_filters(s) * 4; // up to 4 retries per filter
// Up to 7 retries per filter (channel, rate, 5x format conversions)
int max_retry = af_count_filters(s) * 7;
int retry = 0;
while (af) {
if (retry >= max_retry)

136
audio/filter/af_convert24.c Normal file
View File

@ -0,0 +1,136 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "audio/format.h"
#include "af.h"
static bool test_conversion(int src_format, int dst_format)
{
if (!(src_format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I)
return false;
if ((src_format & ~AF_FORMAT_BITS_MASK) !=
(dst_format & ~AF_FORMAT_BITS_MASK))
return false;
int srcbits = src_format & AF_FORMAT_BITS_MASK;
int dstbits = dst_format & AF_FORMAT_BITS_MASK;
return (srcbits == AF_FORMAT_24BIT && dstbits == AF_FORMAT_32BIT) ||
(srcbits == AF_FORMAT_32BIT && dstbits == AF_FORMAT_24BIT);
}
static int control(struct af_instance *af, int cmd, void *arg)
{
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
if (!test_conversion(in->format, out->format))
return AF_DETACH;
if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_24BIT) {
mp_audio_set_format(out, af_fmt_change_bits(in->format, 32));
} else if ((in->format & AF_FORMAT_BITS_MASK) == AF_FORMAT_32BIT) {
mp_audio_set_format(out, af_fmt_change_bits(in->format, 24));
} else {
abort();
}
out->rate = in->rate;
mp_audio_set_channels(out, &in->channels);
assert(test_conversion(in->format, out->format));
af->mul = (double)out->bps / in->bps;
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: {
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// The LSB is always ignored.
#if BYTE_ORDER == BIG_ENDIAN
#define SHIFT(x) ((3-(x))*8)
#else
#define SHIFT(x) (((x)+1)*8)
#endif
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
{
if (RESIZE_LOCAL_BUFFER(af, data) != AF_OK)
return NULL;
struct mp_audio *out = af->data;
size_t len = data->len / data->bps;
if (data->bps == 4) {
for (int s = 0; s < len; s++) {
uint32_t val = *((uint32_t *)data->audio + s);
uint8_t *ptr = (uint8_t *)out->audio + s * 3;
ptr[0] = val >> SHIFT(0);
ptr[1] = val >> SHIFT(1);
ptr[2] = val >> SHIFT(2);
}
mp_audio_set_format(data, af_fmt_change_bits(data->format, 24));
} else {
for (int s = 0; s < len; s++) {
uint8_t *ptr = (uint8_t *)data->audio + s * 3;
uint32_t val = ptr[0] << SHIFT(0)
| ptr[1] << SHIFT(1)
| ptr[2] << SHIFT(2);
*((uint32_t *)out->audio + s) = val;
}
mp_audio_set_format(data, af_fmt_change_bits(data->format, 32));
}
data->audio = out->audio;
data->len = len * data->bps;
return data;
}
static void uninit(struct af_instance* af)
{
if (af->data)
free(af->data->audio);
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->play = play;
af->uninit = uninit;
af->data = talloc_zero(af, struct mp_audio);
return AF_OK;
}
struct af_info af_info_convert24 = {
"Convert between 24 and 32 bit sample format",
"convert24",
"",
"",
0,
af_open,
.test_conversion = test_conversion,
};

View File

@ -0,0 +1,133 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "af.h"
#include "audio/format.h"
#include "compat/mpbswap.h"
static bool test_conversion(int src_format, int dst_format)
{
int src_noend = src_format & ~AF_FORMAT_END_MASK;
int dst_noend = dst_format & ~AF_FORMAT_END_MASK;
// We can swap endian for all formats, but sign only for integer formats.
if (src_noend == dst_noend)
return true;
if (((src_noend & ~AF_FORMAT_SIGN_MASK) ==
(dst_noend & ~AF_FORMAT_SIGN_MASK)) &&
((src_noend & AF_FORMAT_POINT_MASK) == AF_FORMAT_I))
return true;
return false;
}
static int control(struct af_instance *af, int cmd, void *arg)
{
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
if (!test_conversion(in->format, out->format))
return AF_DETACH;
out->rate = in->rate;
mp_audio_set_channels(out, &in->channels);
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: {
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
static void endian(void *data, int len, int bps)
{
switch (bps) {
case 2:
for (int i = 0; i < len; i++) {
((uint16_t*)data)[i] = bswap_16(((uint16_t *)data)[i]);
}
break;
case 3:
for(int i = 0; i < len; i++) {
uint8_t s = ((uint8_t *)data)[3 * i];
((uint8_t *)data)[3 * i] = ((uint8_t *)data)[3 * i + 2];
((uint8_t *)data)[3 * i + 2] = s;
}
break;
case 4:
for(int i = 0; i < len; i++) {
((uint32_t*)data)[i] = bswap_32(((uint32_t *)data)[i]);
}
break;
}
}
static void si2us(void *data, int len, int bps, bool le)
{
ptrdiff_t i = -(len * bps);
uint8_t *p = &((uint8_t *)data)[len * bps];
if (le && bps > 1)
p += bps - 1;
if (len <= 0)
return;
do {
p[i] ^= 0x80;
} while (i += bps);
}
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
{
int infmt = data->format;
int outfmt = af->data->format;
size_t len = data->len / data->bps;
if ((infmt & AF_FORMAT_END_MASK) != (outfmt & AF_FORMAT_END_MASK))
endian(data->audio, len, data->bps);
if ((infmt & AF_FORMAT_SIGN_MASK) != (outfmt & AF_FORMAT_SIGN_MASK))
si2us(data->audio, len, data->bps,
(outfmt & AF_FORMAT_END_MASK) == AF_FORMAT_LE);
mp_audio_set_format(data, outfmt);
return data;
}
static int af_open(struct af_instance *af)
{
af->control = control;
af->play = play;
af->mul = 1;
af->data = talloc_zero(af, struct mp_audio);
return AF_OK;
}
struct af_info af_info_convertsignendian = {
"Convert between sample format sign/endian",
"convertsignendian",
"",
"",
0,
af_open,
.test_conversion = test_conversion,
};

View File

@ -72,12 +72,12 @@ static int control(struct af_instance *af, int cmd, void *arg)
if (in->nch != out->nch || in->bps != out->bps) {
mp_msg(MSGT_AFILTER, MSGL_ERR,
"[af_force] Forced input/output formats are incompatible.\n");
"[af_format] Forced input/output formats are incompatible.\n");
return AF_ERROR;
}
if (priv->fail) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[af_force] Failing on purpose.\n");
mp_msg(MSGT_AFILTER, MSGL_ERR, "[af_format] Failing on purpose.\n");
return AF_ERROR;
}
@ -116,9 +116,9 @@ static int af_open(struct af_instance *af)
#define OPT_BASE_STRUCT struct priv
struct af_info af_info_force = {
struct af_info af_info_format = {
"Force audio format",
"force",
"format",
"",
"",
0,

View File

@ -1,494 +0,0 @@
/*
* This audio filter changes the format of a data block. Valid
* formats are: AFMT_U8, AFMT_S8, AFMT_S16_LE, AFMT_S16_BE
* AFMT_U16_LE, AFMT_U16_BE, AFMT_S32_LE and AFMT_S32_BE.
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <sys/types.h>
#include "config.h"
#include "af.h"
#include "compat/mpbswap.h"
/* Functions used by play to convert the input audio to the correct
format */
// Switch endianness
static void endian(void* in, void* out, int len, int bps);
// From signed to unsigned and the other way
static void si2us(void* data, int len, int bps);
// Change the number of bits per sample
static void change_bps(void* in, void* out, int len, int inbps, int outbps);
// From float to int signed
static void float2int(float* in, void* out, int len, int bps);
// From signed int to float
static void int2float(void* in, float* out, int len, int bps);
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data);
static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data);
// Helper functions to check sanity for input arguments
// Sanity check for bytes per sample
static int check_bps(int bps)
{
if(bps != 4 && bps != 3 && bps != 2 && bps != 1){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] The number of bytes per sample"
" must be 1, 2, 3 or 4. Current value is %i \n",bps);
return AF_ERROR;
}
return AF_OK;
}
// Check for unsupported formats
static int check_format(int format)
{
char buf[256];
if ((format & AF_FORMAT_SPECIAL_MASK) == 0)
return AF_OK;
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] Sample format %s not yet supported \n",
af_fmt2str(format,buf,256));
return AF_ERROR;
}
static bool test_conversion(int src_format, int dst_format)
{
// This is the fallback conversion filter, so this filter is always
// inserted on format mismatches if no other filter can handle it.
// Initializing the filter might still fail.
return true;
}
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
switch(cmd){
case AF_CONTROL_REINIT:{
char buf1[256];
char buf2[256];
struct mp_audio *data = arg;
int supported_ac3 = 0;
// Make sure this filter isn't redundant
if(af->data->format == data->format)
return AF_DETACH;
// A bit complex because we can convert AC3
// to generic iec61937 but not the other way
// round.
if (AF_FORMAT_IS_AC3(af->data->format))
supported_ac3 = AF_FORMAT_IS_AC3(data->format);
else if (AF_FORMAT_IS_IEC61937(af->data->format))
supported_ac3 = AF_FORMAT_IS_IEC61937(data->format);
// Allow trivial AC3-endianness conversion
if (!supported_ac3)
// Check for errors in configuration
if((AF_OK != check_bps(data->bps)) ||
(AF_OK != check_format(data->format)) ||
(AF_OK != check_bps(af->data->bps)) ||
(AF_OK != check_format(af->data->format)))
return AF_ERROR;
af_fmt2str(data->format,buf1,256);
af_fmt2str(af->data->format,buf2,256);
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Changing sample format from %s to %s\n",
buf1, buf2);
af->data->rate = data->rate;
mp_audio_set_channels(af->data, &data->channels);
af->mul = (double)af->data->bps / data->bps;
af->play = play; // set default
// look whether only endianness differences are there
if ((af->data->format & ~AF_FORMAT_END_MASK) ==
(data->format & ~AF_FORMAT_END_MASK))
{
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated endianness conversion only\n");
af->play = play_swapendian;
}
if ((data->format == AF_FORMAT_FLOAT_NE) &&
(af->data->format == AF_FORMAT_S16_NE))
{
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n",
buf1, buf2);
af->play = play_float_s16;
}
if ((data->format == AF_FORMAT_S16_NE) &&
(af->data->format == AF_FORMAT_FLOAT_NE))
{
mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n",
buf1, buf2);
af->play = play_s16_float;
}
return AF_OK;
}
case AF_CONTROL_COMMAND_LINE:{
int format = af_str2fmt_short(bstr0(arg));
if (!format) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] %s is not a valid format\n", (char *)arg);
return AF_ERROR;
}
if(AF_OK != af->control(af, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
return AF_ERROR;
return AF_OK;
}
case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET:{
// Check for errors in configuration
if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg))
return AF_ERROR;
mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
}
return AF_UNKNOWN;
}
// Deallocate memory
static void uninit(struct af_instance* af)
{
if (af->data)
free(af->data->audio);
free(af->data);
af->setup = 0;
}
static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/c->bps; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
endian(c->audio,l->audio,len,c->bps);
c->audio = l->audio;
mp_audio_set_format(c, l->format);
return c;
}
static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/4; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
float2int(c->audio, l->audio, len, 2);
c->audio = l->audio;
mp_audio_set_format(c, l->format);
c->len = len*2;
return c;
}
static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/2; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
int2float(c->audio, l->audio, len, 2);
c->audio = l->audio;
mp_audio_set_format(c, l->format);
c->len = len*4;
return c;
}
// Filter data through filter
static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
{
struct mp_audio* l = af->data; // Local data
struct mp_audio* c = data; // Current working data
int len = c->len/c->bps; // Length in samples of current audio block
if(AF_OK != RESIZE_LOCAL_BUFFER(af,data))
return NULL;
// Change to cpu native endian format
if((c->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE)
endian(c->audio,c->audio,len,c->bps);
// Conversion table
if((c->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) {
float2int(c->audio, l->audio, len, l->bps);
if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
si2us(l->audio,len,l->bps);
} else {
// Input must be int
// Change signed/unsigned
if((c->format&AF_FORMAT_SIGN_MASK) != (l->format&AF_FORMAT_SIGN_MASK)){
si2us(c->audio,len,c->bps);
}
// Convert to special formats
switch(l->format&AF_FORMAT_POINT_MASK){
case(AF_FORMAT_F):
int2float(c->audio, l->audio, len, c->bps);
break;
default:
// Change the number of bits
if(c->bps != l->bps)
change_bps(c->audio,l->audio,len,c->bps,l->bps);
else
memcpy(l->audio,c->audio,len*c->bps);
break;
}
}
// Switch from cpu native endian to the correct endianness
if((l->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE)
endian(l->audio,l->audio,len,l->bps);
// Set output data
c->audio = l->audio;
mp_audio_set_format(c, l->format);
c->len = len*l->bps;
return c;
}
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
af->control=control;
af->uninit=uninit;
af->play=play;
af->mul=1;
af->data=calloc(1,sizeof(struct mp_audio));
if(af->data == NULL)
return AF_ERROR;
return AF_OK;
}
// Description of this filter
struct af_info af_info_format = {
"Sample format conversion",
"format",
"Anders",
"",
AF_FLAGS_REENTRANT,
af_open,
.test_conversion = test_conversion,
};
static inline uint32_t load24bit(void* data, int pos) {
#if BYTE_ORDER == BIG_ENDIAN
return (((uint32_t)((uint8_t*)data)[3*pos])<<24) |
(((uint32_t)((uint8_t*)data)[3*pos+1])<<16) |
(((uint32_t)((uint8_t*)data)[3*pos+2])<<8);
#else
return (((uint32_t)((uint8_t*)data)[3*pos])<<8) |
(((uint32_t)((uint8_t*)data)[3*pos+1])<<16) |
(((uint32_t)((uint8_t*)data)[3*pos+2])<<24);
#endif
}
static inline void store24bit(void* data, int pos, uint32_t expanded_value) {
#if BYTE_ORDER == BIG_ENDIAN
((uint8_t*)data)[3*pos]=expanded_value>>24;
((uint8_t*)data)[3*pos+1]=expanded_value>>16;
((uint8_t*)data)[3*pos+2]=expanded_value>>8;
#else
((uint8_t*)data)[3*pos]=expanded_value>>8;
((uint8_t*)data)[3*pos+1]=expanded_value>>16;
((uint8_t*)data)[3*pos+2]=expanded_value>>24;
#endif
}
// Function implementations used by play
static void endian(void* in, void* out, int len, int bps)
{
register int i;
switch(bps){
case(2):{
for(i=0;i<len;i++){
((uint16_t*)out)[i]=bswap_16(((uint16_t*)in)[i]);
}
break;
}
case(3):{
register uint8_t s;
for(i=0;i<len;i++){
s=((uint8_t*)in)[3*i];
((uint8_t*)out)[3*i]=((uint8_t*)in)[3*i+2];
if (in != out)
((uint8_t*)out)[3*i+1]=((uint8_t*)in)[3*i+1];
((uint8_t*)out)[3*i+2]=s;
}
break;
}
case(4):{
for(i=0;i<len;i++){
((uint32_t*)out)[i]=bswap_32(((uint32_t*)in)[i]);
}
break;
}
}
}
static void si2us(void* data, int len, int bps)
{
register long i = -(len * bps);
register uint8_t *p = &((uint8_t *)data)[len * bps];
#if AF_FORMAT_NE == AF_FORMAT_LE
p += bps - 1;
#endif
if (len <= 0) return;
do {
p[i] ^= 0x80;
} while (i += bps);
}
static void change_bps(void* in, void* out, int len, int inbps, int outbps)
{
register int i;
switch(inbps){
case(1):
switch(outbps){
case(2):
for(i=0;i<len;i++)
((uint16_t*)out)[i]=((uint16_t)((uint8_t*)in)[i])<<8;
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, ((uint32_t)((uint8_t*)in)[i])<<24);
break;
case(4):
for(i=0;i<len;i++)
((uint32_t*)out)[i]=((uint32_t)((uint8_t*)in)[i])<<24;
break;
}
break;
case(2):
switch(outbps){
case(1):
for(i=0;i<len;i++)
((uint8_t*)out)[i]=(uint8_t)((((uint16_t*)in)[i])>>8);
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, ((uint32_t)((uint16_t*)in)[i])<<16);
break;
case(4):
for(i=0;i<len;i++)
((uint32_t*)out)[i]=((uint32_t)((uint16_t*)in)[i])<<16;
break;
}
break;
case(3):
switch(outbps){
case(1):
for(i=0;i<len;i++)
((uint8_t*)out)[i]=(uint8_t)(load24bit(in, i)>>24);
break;
case(2):
for(i=0;i<len;i++)
((uint16_t*)out)[i]=(uint16_t)(load24bit(in, i)>>16);
break;
case(4):
for(i=0;i<len;i++)
((uint32_t*)out)[i]=(uint32_t)load24bit(in, i);
break;
}
break;
case(4):
switch(outbps){
case(1):
for(i=0;i<len;i++)
((uint8_t*)out)[i]=(uint8_t)((((uint32_t*)in)[i])>>24);
break;
case(2):
for(i=0;i<len;i++)
((uint16_t*)out)[i]=(uint16_t)((((uint32_t*)in)[i])>>16);
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, ((uint32_t*)in)[i]);
break;
}
break;
}
}
static void float2int(float* in, void* out, int len, int bps)
{
register int i;
switch(bps){
case(1):
for(i=0;i<len;i++)
((int8_t*)out)[i] = lrintf(127.0 * clamp(in[i], -1.0f, +1.0f));
break;
case(2):
for(i=0;i<len;i++)
((int16_t*)out)[i] = lrintf(32767.0 * clamp(in[i], -1.0f, +1.0f));
break;
case(3):
for(i=0;i<len;i++)
store24bit(out, i, lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f)));
break;
case(4):
for(i=0;i<len;i++)
((int32_t*)out)[i] = lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f));
break;
}
}
static void int2float(void* in, float* out, int len, int bps)
{
register int i;
switch(bps){
case(1):
for(i=0;i<len;i++)
out[i]=(1.0/128.0)*((int8_t*)in)[i];
break;
case(2):
for(i=0;i<len;i++)
out[i]=(1.0/32768.0)*((int16_t*)in)[i];
break;
case(3):
for(i=0;i<len;i++)
out[i]=(1.0/2147483648.0)*((int32_t)load24bit(in, i));
break;
case(4):
for(i=0;i<len;i++)
out[i]=(1.0/2147483648.0)*((int32_t*)in)[i];
break;
}
}

View File

@ -1808,7 +1808,7 @@ bool m_obj_list_find(struct m_obj_desc *dst, const struct m_obj_list *l,
// Assume it's deprecated in this case.
// Also, it's used by the VO code only, so whatever.
mp_msg(MSGT_CFGPARSER, MSGL_WARN,
"VO driver '%s' has been replaced with '%s'!\n",
"Driver '%s' has been replaced with '%s'!\n",
aname, alias);
}
return true;