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:
parent
33707c6d63
commit
e60b8f181d
@ -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
|
||||
|
3
Makefile
3
Makefile
@ -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 \
|
||||
|
@ -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
136
audio/filter/af_convert24.c
Normal 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,
|
||||
};
|
133
audio/filter/af_convertsignendian.c
Normal file
133
audio/filter/af_convertsignendian.c
Normal 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,
|
||||
};
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user