aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar wm4 <wm4@nowhere>2013-05-12 21:47:55 +0200
committerGravatar wm4 <wm4@nowhere>2013-05-12 21:47:55 +0200
commite6e5a7b221ef2fcdd5a1982d6fdcb627100447d2 (patch)
tree08b54ef9bb771434fc7fbe9185793503d3ba314c
parent6a83ef1552de4a1a71da49e45647ce1a4ce64e53 (diff)
parent48f94311516dc1426644b3e68b2a48c22727e1e7 (diff)
Merge branch 'audio_changes'
Conflicts: audio/out/ao_lavc.c
-rw-r--r--DOCS/man/en/af.rst58
-rw-r--r--DOCS/man/en/changes.rst1
-rw-r--r--DOCS/man/en/options.rst87
-rw-r--r--Makefile5
-rw-r--r--TOOLS/uncrustify.cfg7
-rw-r--r--audio/audio.c76
-rw-r--r--audio/audio.h46
-rw-r--r--audio/chmap.c486
-rw-r--r--audio/chmap.h132
-rw-r--r--audio/chmap_sel.c210
-rw-r--r--audio/chmap_sel.h43
-rw-r--r--audio/decode/ad.h6
-rw-r--r--audio/decode/ad_lavc.c85
-rw-r--r--audio/decode/ad_mpg123.c2
-rw-r--r--audio/decode/ad_spdif.c17
-rw-r--r--audio/decode/dec_audio.c72
-rw-r--r--audio/decode/dec_audio.h4
-rw-r--r--audio/filter/af.c1078
-rw-r--r--audio/filter/af.h63
-rw-r--r--audio/filter/af_bs2b.c8
-rw-r--r--audio/filter/af_center.c5
-rw-r--r--audio/filter/af_channels.c47
-rw-r--r--audio/filter/af_delay.c5
-rw-r--r--audio/filter/af_drc.c13
-rw-r--r--audio/filter/af_dummy.c4
-rw-r--r--audio/filter/af_equalizer.c30
-rw-r--r--audio/filter/af_export.c6
-rw-r--r--audio/filter/af_extrastereo.c17
-rw-r--r--audio/filter/af_force.c146
-rw-r--r--audio/filter/af_format.c32
-rw-r--r--audio/filter/af_hrtf.c11
-rw-r--r--audio/filter/af_karaoke.c6
-rw-r--r--audio/filter/af_ladspa.c38
-rw-r--r--audio/filter/af_lavcac3enc.c29
-rw-r--r--audio/filter/af_lavrresample.c171
-rw-r--r--audio/filter/af_pan.c24
-rw-r--r--audio/filter/af_scaletempo.c14
-rw-r--r--audio/filter/af_sinesuppress.c19
-rw-r--r--audio/filter/af_sub.c5
-rw-r--r--audio/filter/af_surround.c12
-rw-r--r--audio/filter/af_sweep.c8
-rw-r--r--audio/filter/af_tools.c4
-rw-r--r--audio/filter/af_volume.c33
-rw-r--r--audio/filter/control.h127
-rw-r--r--audio/fmt-conversion.c65
-rw-r--r--audio/fmt-conversion.h25
-rw-r--r--audio/format.c18
-rw-r--r--audio/format.h9
-rw-r--r--audio/mixer.c2
-rw-r--r--audio/out/ao.c12
-rw-r--r--audio/out/ao.h14
-rw-r--r--audio/out/ao_alsa.c1345
-rw-r--r--audio/out/ao_coreaudio.c19
-rw-r--r--audio/out/ao_dsound.c91
-rw-r--r--audio/out/ao_jack.c23
-rw-r--r--audio/out/ao_lavc.c85
-rw-r--r--audio/out/ao_null.c11
-rw-r--r--audio/out/ao_openal.c82
-rw-r--r--audio/out/ao_oss.c36
-rw-r--r--audio/out/ao_pcm.c38
-rw-r--r--audio/out/ao_portaudio.c11
-rw-r--r--audio/out/ao_pulse.c106
-rw-r--r--audio/out/ao_rsound.c13
-rw-r--r--audio/out/ao_sdl.c18
-rw-r--r--audio/out/audio_out_internal.h3
-rw-r--r--audio/reorder_ch.c1373
-rw-r--r--audio/reorder_ch.h108
-rw-r--r--core/cfg-mplayer.h13
-rw-r--r--core/command.c16
-rw-r--r--core/defaultopts.c8
-rw-r--r--core/m_option.c42
-rw-r--r--core/m_option.h6
-rw-r--r--core/mplayer.c24
-rw-r--r--core/options.h9
-rw-r--r--demux/demux_lavf.c4
-rw-r--r--demux/demux_mkv.c6
-rw-r--r--demux/demux_rawaudio.c13
-rw-r--r--demux/stheader.h5
-rw-r--r--stream/tv.c10
79 files changed, 3341 insertions, 3614 deletions
diff --git a/DOCS/man/en/af.rst b/DOCS/man/en/af.rst
index 2eaeabed0c..08e7853990 100644
--- a/DOCS/man/en/af.rst
+++ b/DOCS/man/en/af.rst
@@ -30,15 +30,15 @@ filter list.
Available filters are:
lavrresample[=option1:option2:...]
- Changes the sample rate of the audio stream to an integer <srate> in Hz.
- Can be used if you have a fixed frequency sound card or if you are stuck
- with an old sound card that is only capable of max 44.1kHz.
+ This filter uses libavresample (or libswresample, depending on the build)
+ to change sample rate, sample format, or channel layout of the audio stream.
+ This filter is automatically enabled if the audio output doesn't support
+ the audio configuration of the file being played.
- This filter is automatically enabled if necessary. It only supports the
- 16-bit integer native-endian format.
+ It supports only the following sample formats: u8, s16ne, s32ne, floatne.
srate=<srate>
- the output sample rate (defaut: 44100)
+ the output sample rate
length=<length>
length of the filter with respect to the lower sampling rate (default:
16)
@@ -50,6 +50,11 @@ lavrresample[=option1:option2:...]
linear
if set then filters will be linearly interpolated between polyphase
entries (default: no)
+ no-detach
+ don't detach if input and output audio format/rate/channels are the
+ same. You should add this option if you specify additional parameters,
+ as automatically inserted lavrresample instances will use the
+ default settings.
lavcac3enc[=tospdif[:bitrate[:minchn]]]
Encode multi-channel audio to AC-3 at runtime using libavcodec. Supports
@@ -205,6 +210,34 @@ channels=nch[:nr:from1:to1:from2:to2:from3:to3:...]
Would change the number of channels to 6 and set up 4 routes that 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
+ 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.
+
+ All parameters are optional. The ``in-`` variants restrict what the filter
+ accepts as input. The ``out-`` variants change the audio format, without
+ 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.
+
+ <in-srate>
+ Force conversion to a specific sample rate. The rate is an integer,
+ 48000 for example.
+
+ <in-channels>
+ Force mixing to a specific channel layout. See ``--channels`` option
+ for possible values.
+
+ <out-format>
+
+ <out-srate>
+
+ <out-channels>
+
format[=format]
Convert between different sample formats. Automatically enabled when
needed by the sound card or another filter. See also ``--format``.
@@ -219,7 +252,7 @@ format[=format]
rule that are also valid format specifiers: u8, s8, floatle, floatbe,
floatne, mpeg2, and ac3.
-volume[=v[:sc]]
+volume[=v[:sc[:fast]]]
Implements software volume control. Use this filter with caution since it
can reduce the signal to noise ratio of the sound. In most cases it is
best to set the level for the PCM sound to max, leave this filter out and
@@ -233,8 +266,7 @@ volume[=v[:sc]]
This filter has a second feature: It measures the overall maximum sound
level and prints out that level when mpv exits. This feature currently
- only works with floating-point data, use e.g. ``--af-adv=force=5``, or use
- ``--af=stats``.
+ only works with floating-point data.
*NOTE*: This filter is not reentrant and can therefore only be enabled
once for every audio stream.
@@ -250,6 +282,9 @@ volume[=v[:sc]]
*WARNING*: This feature creates distortion and should be considered a
last resort.
+ <fast>
+ Force S16 sample format if set to 1. Lower quality, but might be faster
+ in some situations.
*EXAMPLE*:
@@ -286,6 +321,11 @@ pan=n[:L00:L01:L02:...L10:L11:L12:...Ln0:Ln1:Ln2:...]
channels 0 and 1 into output channel 2 (which could be sent to a
subwoofer for example).
+ *NOTE*: if you just want to force remixing to a certain output channel
+ layout, it's easier to use the ``force`` filter. For example,
+ ``mpv '--af=force=channels=5.1' '--channels=5.1'`` would always force
+ remixing audio to 5.1 and output it like this.
+
sub[=fc:ch]
Adds a subwoofer channel to the audio stream. The audio data used for
creating the subwoofer channel is an average of the sound in channel 0 and
diff --git a/DOCS/man/en/changes.rst b/DOCS/man/en/changes.rst
index 1e5bb74a2c..20f6be553a 100644
--- a/DOCS/man/en/changes.rst
+++ b/DOCS/man/en/changes.rst
@@ -124,6 +124,7 @@ Command line switches
-afm hwac3 --ad=spdif:ac3,spdif:dts
-x W, -y H --geometry=WxH + --no-keepaspect
-xy W --autofit=W
+ -a52drc level --ad-lavc-ac3drc=level
=================================== ===================================
*NOTE*: ``-opt val`` becomes ``--opt=val``.
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index d49acaf03c..cd1b4ab90a 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -1,11 +1,3 @@
---a52drc=<level>
- Select the Dynamic Range Compression level for AC-3 audio streams. <level>
- is a float value ranging from 0 to 1, where 0 means no compression and 1
- (which is the default) means full compression (make loud passages more
- silent and vice versa). Values up to 2 are also accepted, but are purely
- experimental. This option only shows an effect if the AC-3 stream contains
- the required range compression information.
-
--abs=<value>
(``--ao=oss`` only) (OBSOLETE)
Override audio driver/card buffer size detection.
@@ -37,6 +29,25 @@
``--ad=help``
List all available decoders.
+--ad-lavc-ac3drc=<level>
+ Select the Dynamic Range Compression level for AC-3 audio streams. <level>
+ is a float value ranging from 0 to 1, where 0 means no compression and 1
+ (which is the default) means full compression (make loud passages more
+ silent and vice versa). Values up to 2 are also accepted, but are purely
+ experimental. This option only shows an effect if the AC-3 stream contains
+ the required range compression information.
+
+--ad-lavc-downmix=<yes|no>
+ Whether to request audio channel downmixing from the decoder (default: yes).
+ Some decoders, like AC-3, AAC and DTS, can remix audio on decoding. The
+ requested number of output channels is set with the ``--channels`` option.
+ Useful for playing surround audio on a stereo system.
+
+--ad-lavc-o=<key>=<value>[,<key>=<value>[,...]]
+ Pass AVOptions to libavcodec decoder. Note, a patch to make the o=
+ unneeded and pass all unknown options through the AVOption system is
+ welcome. A full list of AVOptions can be found in the FFmpeg manual.
+
--af=<filter1[=parameter1:parameter2:...],filter2,...>
Specify a list of audio filters to apply to the audio stream. See
`audio_filters` for details and descriptions of the available filters.
@@ -44,40 +55,6 @@
``--af-clr`` exist to modify a previously specified list, but you
shouldn't need these for typical use.
---af-adv=<force=(0-7):list=(filters)>
- See also ``--af``.
- Specify advanced audio filter options:
-
- force=<0-7>
- Forces the insertion of audio filters to one of the following:
-
- 0
- Use completely automatic filter insertion (currently identical to
- 1).
- 1
- Optimize for accuracy (default).
- 2
- Optimize for speed. *Warning*: Some features in the audio filters
- may silently fail, and the sound quality may drop.
- 3
- Use no automatic insertion of filters and no optimization.
- *Warning*: It may be possible to crash mpv using this setting.
- 4
- Use automatic insertion of filters according to 0 above, but use
- floating point processing when possible.
- 5
- Use automatic insertion of filters according to 1 above, but use
- floating point processing when possible.
- 6
- Use automatic insertion of filters according to 2 above, but use
- floating point processing when possible.
- 7
- Use no automatic insertion of filters according to 3 above, and
- use floating point processing when possible.
-
- list=<filters>
- Same as ``--af``.
-
--aid=<ID|auto|no>
Select audio channel. ``auto`` selects the default, ``no`` disables audio.
See also ``--alang``.
@@ -381,25 +358,24 @@
--cdrom-device=<path>
Specify the CD-ROM device (default: ``/dev/cdrom``).
---channels=<number>
+--channels=<number|layout>
Request the number of playback channels (default: 2). mpv asks the
decoder to decode the audio into as many channels as specified. Then it is
up to the decoder to fulfill the requirement. This is usually only
- important when playing videos with AC-3 audio (like DVDs). In that case
- liba52 does the decoding by default and correctly downmixes the audio into
- the requested number of channels. To directly control the number of output
- channels independently of how many channels are decoded, use the channels
- filter (``--af=channels``).
+ important when playing videos with AC-3, AAC or DTS audio. In that case
+ libavcodec downmixes the audio into the requested number of channels if
+ possible.
*NOTE*: This option is honored by codecs (AC-3 only), filters (surround)
and audio output drivers (OSS at least).
- Available options are:
+ The ``--channels`` option either takes a channel number or an explicit
+ channel layout. Channel numbers refer to default layouts, e.g. 2 channels
+ refer to stereo, 6 refers to 5.1.
- :2: stereo
- :4: surround
- :6: full 5.1
- :8: full 7.1
+ See ``--channels=help`` output for defined default layouts. This also
+ lists speaker names, which can be used to express arbitrary channel
+ layouts (e.g. ``fl-fr-lfe`` is 2.1).
--chapter=<start[-end]>
Specify which chapter to start playing at. Optionally specify which
@@ -1985,9 +1961,8 @@
--srate=<Hz>
Select the output sample rate to be used (of course sound cards have
limits on this). If the sample frequency selected is different from that
- of the current media, the resample or lavcresample audio filter will be
- inserted into the audio filter layer to compensate for the difference. The
- type of resampling can be controlled by the ``--af-adv`` option.
+ of the current media, the lavrresample audio filter will be
+ inserted into the audio filter layer to compensate for the difference.
--start=<relative time>
Seek to given time position.
diff --git a/Makefile b/Makefile
index 314bbb4fb7..76040a38f1 100644
--- a/Makefile
+++ b/Makefile
@@ -124,6 +124,10 @@ ifeq ($(HAVE_AVUTIL_REFCOUNTING),no)
endif
SOURCES = talloc.c \
+ audio/audio.c \
+ audio/chmap.c \
+ audio/chmap_sel.c \
+ audio/fmt-conversion.c \
audio/format.c \
audio/mixer.c \
audio/reorder_ch.c \
@@ -138,6 +142,7 @@ SOURCES = talloc.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 \
diff --git a/TOOLS/uncrustify.cfg b/TOOLS/uncrustify.cfg
index b5133be203..837d9a1770 100644
--- a/TOOLS/uncrustify.cfg
+++ b/TOOLS/uncrustify.cfg
@@ -1,3 +1,10 @@
+# Usage:
+# uncrustify -l C -c TOOLS/uncrustify.cfg --no-backup --replace file.c
+#
+# Keep in mind that this uncrustify configuration still produces some
+# bad/broken formatting.
+#
+
code_width=80
indent_align_string=false
indent_braces=false
diff --git a/audio/audio.c b/audio/audio.c
new file mode 100644
index 0000000000..c9d5c9231c
--- /dev/null
+++ b/audio/audio.c
@@ -0,0 +1,76 @@
+/*
+ * 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 <assert.h>
+
+#include "core/mp_talloc.h"
+#include "audio.h"
+
+void mp_audio_set_format(struct mp_audio *mpa, int format)
+{
+ mpa->format = format;
+ mpa->bps = af_fmt2bits(format) / 8;
+}
+
+void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
+{
+ struct mp_chmap map;
+ mp_chmap_from_channels(&map, num_channels);
+ mp_audio_set_channels(mpa, &map);
+}
+
+// Use old MPlayer/ALSA channel layout.
+void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
+{
+ struct mp_chmap map;
+ mp_chmap_from_channels_alsa(&map, num_channels);
+ mp_audio_set_channels(mpa, &map);
+}
+
+void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap)
+{
+ assert(mp_chmap_is_empty(chmap) || mp_chmap_is_valid(chmap));
+ mpa->channels = *chmap;
+ mpa->nch = mpa->channels.num;
+}
+
+void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src)
+{
+ mp_audio_set_format(dst, src->format);
+ mp_audio_set_channels(dst, &src->channels);
+ dst->rate = src->rate;
+}
+
+bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b)
+{
+ return a->format == b->format && a->rate == b->rate &&
+ mp_chmap_equals(&a->channels, &b->channels);
+}
+
+char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format)
+{
+ char *chstr = mp_chmap_to_str(chmap);
+ char *res = talloc_asprintf(NULL, "%dHz %s %dch %s", srate, chstr,
+ chmap->num, af_fmt2str_short(format));
+ talloc_free(chstr);
+ return res;
+}
+
+char *mp_audio_config_to_str(struct mp_audio *mpa)
+{
+ return mp_audio_fmt_to_str(mpa->rate, &mpa->channels, mpa->format);
+}
diff --git a/audio/audio.h b/audio/audio.h
new file mode 100644
index 0000000000..de35e697c8
--- /dev/null
+++ b/audio/audio.h
@@ -0,0 +1,46 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MP_AUDIO_H
+#define MP_AUDIO_H
+
+#include "format.h"
+#include "chmap.h"
+
+// Audio data chunk
+struct mp_audio {
+ void *audio; // data buffer
+ int len; // buffer length (in bytes)
+ int rate; // sample rate
+ struct mp_chmap channels; // channel layout, use mp_audio_set_*() to set
+ int format; // format (AF_FORMAT_...), use mp_audio_set_format() to set
+ // Redundant fields, for convenience
+ int nch; // number of channels (redundant with chmap)
+ int bps; // bytes per sample (redundant with format)
+};
+
+void mp_audio_set_format(struct mp_audio *mpa, int format);
+void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels);
+void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels);
+void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap);
+void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src);
+bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b);
+
+char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format);
+char *mp_audio_config_to_str(struct mp_audio *mpa);
+
+#endif
diff --git a/audio/chmap.c b/audio/chmap.c
new file mode 100644
index 0000000000..fcdb95edb3
--- /dev/null
+++ b/audio/chmap.c
@@ -0,0 +1,486 @@
+/*
+ * 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 "core/mp_msg.h"
+#include "chmap.h"
+
+// Names taken from libavutil/channel_layout.c (Not accessible by API.)
+// Use of these names is hard-coded in some places (e.g. ao_alsa.c)
+static const char *speaker_names[MP_SPEAKER_ID_COUNT][2] = {
+ [MP_SPEAKER_ID_FL] = {"fl", "front left"},
+ [MP_SPEAKER_ID_FR] = {"fr", "front right"},
+ [MP_SPEAKER_ID_FC] = {"fc", "front center"},
+ [MP_SPEAKER_ID_LFE] = {"lfe", "low frequency"},
+ [MP_SPEAKER_ID_BL] = {"bl", "back left"},
+ [MP_SPEAKER_ID_BR] = {"br", "back right"},
+ [MP_SPEAKER_ID_FLC] = {"flc", "front left-of-center"},
+ [MP_SPEAKER_ID_FRC] = {"frc", "front right-of-center"},
+ [MP_SPEAKER_ID_BC] = {"bc", "back center"},
+ [MP_SPEAKER_ID_SL] = {"sl", "side left"},
+ [MP_SPEAKER_ID_SR] = {"sr", "side right"},
+ [MP_SPEAKER_ID_TC] = {"tc", "top center"},
+ [MP_SPEAKER_ID_TFL] = {"tfl", "top front left"},
+ [MP_SPEAKER_ID_TFC] = {"tfc", "top front center"},
+ [MP_SPEAKER_ID_TFR] = {"tfr", "top front right"},
+ [MP_SPEAKER_ID_TBL] = {"tbl", "top back left"},
+ [MP_SPEAKER_ID_TBC] = {"tbc", "top back center"},
+ [MP_SPEAKER_ID_TBR] = {"tbr", "top back right"},
+ [MP_SPEAKER_ID_DL] = {"dl", "downmix left"},
+ [MP_SPEAKER_ID_DR] = {"dr", "downmix right"},
+ [MP_SPEAKER_ID_WL] = {"wl", "wide left"},
+ [MP_SPEAKER_ID_WR] = {"wr", "wide right"},
+ [MP_SPEAKER_ID_SDL] = {"sdl", "surround direct left"},
+ [MP_SPEAKER_ID_SDR] = {"sdr", "surround direct right"},
+ [MP_SPEAKER_ID_LFE2] = {"lfe2", "low frequency 2"},
+};
+
+// Names taken from libavutil/channel_layout.c (Not accessible by API.)
+// Channel order corresponds to lavc/waveex, except for the alsa entries.
+static const char *std_layout_names[][2] = {
+ {"empty", ""}, // not in lavc
+ {"mono", "fc"},
+ {"stereo", "fl-fr"},
+ {"2.1", "fl-fr-lfe"},
+ {"3.0", "fl-fr-fc"},
+ {"3.0(back)", "fl-fr-bc"},
+ {"4.0", "fl-fr-fc-bc"},
+ {"quad", "fl-fr-bl-br"},
+ {"quad(side)", "fl-fr-sl-sr"},
+ {"3.1", "fl-fr-fc-lfe"},
+ {"5.0", "fl-fr-fc-bl-br"},
+ {"5.0(alsa)", "fl-fr-bl-br-fc"}, // not in lavc
+ {"5.0(side)", "fl-fr-fc-sl-sr"},
+ {"4.1", "fl-fr-fc-lfe-bc"},
+ {"4.1(alsa)", "fl-fr-bl-br-lfe"}, // not in lavc
+ {"5.1", "fl-fr-fc-lfe-bl-br"},
+ {"5.1(alsa)", "fl-fr-bl-br-fc-lfe"}, // not in lavc
+ {"5.1(side)", "fl-fr-fc-lfe-sl-sr"},
+ {"6.0", "fl-fr-fc-bc-sl-sr"},
+ {"6.0(front)", "fl-fr-flc-frc-sl-sr"},
+ {"hexagonal", "fl-fr-fc-bl-br-bc"},
+ {"6.1", "fl-fr-fc-lfe-bl-br-bc"},
+ {"6.1(front)", "fl-fr-lfe-flc-frc-sl-sr"},
+ {"7.0", "fl-fr-fc-bl-br-sl-sr"},
+ {"7.0(front)", "fl-fr-fc-flc-frc-sl-sr"},
+ {"7.1", "fl-fr-fc-lfe-bl-br-sl-sr"},
+ {"7.1(alsa)", "fl-fr-bl-br-fc-lfe-sl-sr"}, // not in lavc
+ {"7.1(wide)", "fl-fr-fc-lfe-bl-br-flc-frc"},
+ {"7.1(wide-side)", "fl-fr-fc-lfe-flc-frc-sl-sr"},
+ {"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
+ {"downmix", "dl-dr"},
+ {0}
+};
+
+static const struct mp_chmap default_layouts[MP_NUM_CHANNELS + 1] = {
+ {0}, // empty
+ MP_CHMAP_INIT_MONO, // mono
+ MP_CHMAP2(FL, FR), // stereo
+ MP_CHMAP3(FL, FR, LFE), // 2.1
+ MP_CHMAP4(FL, FR, FC, BC), // 4.0
+ MP_CHMAP5(FL, FR, FC, BL, BR), // 5.0
+ MP_CHMAP6(FL, FR, FC, LFE, BL, BR), // 5.1
+ MP_CHMAP7(FL, FR, FC, LFE, BL, BR, BC), // 6.1
+ MP_CHMAP8(FL, FR, FC, LFE, BL, BR, SL, SR), // 7.1
+};
+
+// The channel order was lavc/waveex, but differs from lavc for 5, 6 and 8
+// channels. 3 and 7 channels were likely undefined (no ALSA support).
+static const char *mplayer_layouts[MP_NUM_CHANNELS + 1] = {
+ [1] = "mono",
+ [2] = "stereo",
+ [4] = "4.0",
+ [5] = "5.0(alsa)",
+ [6] = "5.1(alsa)",
+ [8] = "7.1(alsa)",
+};
+
+// Returns true if speakers are mapped uniquely, and there's at least 1 channel.
+bool mp_chmap_is_valid(const struct mp_chmap *src)
+{
+ bool mapped[MP_SPEAKER_ID_COUNT] = {0};
+ for (int n = 0; n < src->num; n++) {
+ int sp = src->speaker[n];
+ if (sp >= MP_SPEAKER_ID_COUNT || mapped[sp])
+ return false;
+ mapped[sp] = true;
+ }
+ return src->num > 0;
+}
+
+bool mp_chmap_is_empty(const struct mp_chmap *src)
+{
+ return src->num == 0;
+}
+
+// Return true if the channel map defines the number of the channels only, and
+// the channels have to meaning associated with them.
+bool mp_chmap_is_unknown(const struct mp_chmap *src)
+{
+ for (int n = 0; n < src->num; n++) {
+ int speaker = src->speaker[n];
+ if (speaker >= MP_SPEAKER_ID_UNKNOWN0 &&
+ speaker <= MP_SPEAKER_ID_UNKNOWN_LAST)
+ return true;
+ }
+ return false;
+}
+
+// Note: empty channel maps compare as equal. Invalid ones can equal too.
+bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b)
+{
+ if (a->num != b->num)
+ return false;
+ for (int n = 0; n < a->num; n++) {
+ if (a->speaker[n] != b->speaker[n])
+ return false;
+ }
+ return true;
+}
+
+// Whether they use the same speakers (even if in different order).
+bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b)
+{
+ struct mp_chmap t1 = *a, t2 = *b;
+ mp_chmap_reorder_norm(&t1);
+ mp_chmap_reorder_norm(&t2);
+ return mp_chmap_equals(&t1, &t2);
+}
+
+bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b)
+{
+ if (mp_chmap_equals(a, b))
+ return true;
+ if (a->num == b->num && (mp_chmap_is_unknown(a) || mp_chmap_is_unknown(b)))
+ return true;
+ return false;
+}
+
+bool mp_chmap_is_stereo(const struct mp_chmap *src)
+{
+ static const struct mp_chmap stereo = MP_CHMAP_INIT_STEREO;
+ return mp_chmap_equals(src, &stereo);
+}
+
+static int comp_uint8(const void *a, const void *b)
+{
+ return *(const uint8_t *)a - *(const uint8_t *)b;
+}
+
+// Reorder channels to normal order, with monotonically increasing speaker IDs.
+// We define this order as the same order used with waveex.
+void mp_chmap_reorder_norm(struct mp_chmap *map)
+{
+ uint8_t *arr = &map->speaker[0];
+ qsort(arr, map->num, 1, comp_uint8);
+}
+
+// Set *dst to a standard layout with the given number of channels.
+// If the number of channels is invalid, an invalid map is set, and
+// mp_chmap_is_valid(dst) will return false.
+void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels)
+{
+ if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
+ *dst = (struct mp_chmap) {0};
+ } else {
+ *dst = default_layouts[num_channels];
+ }
+}
+
+// Try to do what mplayer/mplayer2/mpv did before channel layouts were
+// introduced, i.e. get the old default channel order.
+void mp_chmap_from_channels_alsa(struct mp_chmap *dst, int num_channels)
+{
+ if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
+ *dst = (struct mp_chmap) {0};
+ } else {
+ mp_chmap_from_str(dst, bstr0(mplayer_layouts[num_channels]));
+ if (!dst->num)
+ mp_chmap_from_channels(dst, num_channels);
+ }
+}
+
+// Set *dst to an unknown layout for the given numbers of channels.
+// If the number of channels is invalid, an invalid map is set, and
+// mp_chmap_is_valid(dst) will return false.
+void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels)
+{
+ if (num_channels < 0 || num_channels > MP_NUM_CHANNELS) {
+ *dst = (struct mp_chmap) {0};
+ } else {
+ dst->num = num_channels;
+ for (int n = 0; n < dst->num; n++)
+ dst->speaker[n] = MP_SPEAKER_ID_UNKNOWN0 + n;
+ }
+}
+
+// Return channel index of the given speaker, or -1.
+static int mp_chmap_find_speaker(const struct mp_chmap *map, int speaker)
+{
+ for (int n = 0; n < map->num; n++) {
+ if (map->speaker[n] == speaker)
+ return n;
+ }
+ return -1;
+}
+
+static void mp_chmap_remove_speaker(struct mp_chmap *map, int speaker)
+{
+ int index = mp_chmap_find_speaker(map, speaker);
+ if (index >= 0) {
+ for (int n = index; n < map->num - 1; n++)
+ map->speaker[n] = map->speaker[n + 1];
+ map->num--;
+ }
+}
+
+// Some decoders output additional, redundant channels, which are usually
+// useless and will mess up proper audio output channel handling.
+// map: channel map from which the channels should be removed
+// requested: if not NULL, and if it contains any of the "useless" channels,
+// don't remove them (this is for convenience)
+void mp_chmap_remove_useless_channels(struct mp_chmap *map,
+ const struct mp_chmap *requested)
+{
+ if (requested &&
+ mp_chmap_find_speaker(requested, MP_SPEAKER_ID_DL) >= 0)
+ return;
+
+ if (map->num > 2) {
+ mp_chmap_remove_speaker(map, MP_SPEAKER_ID_DL);
+ mp_chmap_remove_speaker(map, MP_SPEAKER_ID_DR);
+ }
+}
+
+// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
+// Warning: this ignores the order of the channels, and will return a channel
+// mask even if the order is different from libavcodec's.
+uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
+{
+ // lavc has no concept for unknown layouts yet, so pick a default
+ struct mp_chmap t = *src;
+ if (mp_chmap_is_unknown(&t))
+ mp_chmap_from_channels(&t, t.num);
+ uint64_t mask = 0;
+ for (int n = 0; n < t.num; n++)
+ mask |= 1ULL << t.speaker[n];
+ return mask;
+}
+
+// Return the ffmpeg/libav channel layout as in <libavutil/channel_layout.h>.
+// Returns 0 if the channel order doesn't match lavc's or if it's invalid.
+uint64_t mp_chmap_to_lavc(const struct mp_chmap *src)
+{
+ if (!mp_chmap_is_lavc(src))
+ return 0;
+ return mp_chmap_to_lavc_unchecked(src);
+}
+
+// Set channel map from the ffmpeg/libav channel layout as in
+// <libavutil/channel_layout.h>.
+// If the number of channels exceed MP_NUM_CHANNELS, set dst to empty.
+void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src)
+{
+ dst->num = 0;
+ for (int n = 0; n < 64; n++) {
+ if (src & (1ULL << n)) {
+ if (dst->num >= MP_NUM_CHANNELS) {
+ dst->num = 0;
+ return;
+ }
+ dst->speaker[dst->num] = n;
+ dst->num++;
+ }
+ }
+}
+
+bool mp_chmap_is_lavc(const struct mp_chmap *src)
+{
+ if (!mp_chmap_is_valid(src))
+ return false;
+ if (mp_chmap_is_unknown(src))
+ return true;
+ // lavc's channel layout is a bit mask, and channels are always ordered
+ // from LSB to MSB speaker bits, so speaker IDs have to increase.
+ assert(src->num > 0);
+ for (int n = 1; n < src->num; n++) {
+ if (src->speaker[n - 1] >= src->speaker[n])
+ return false;
+ }
+ for (int n = 0; n < src->num; n++) {
+ if (src->speaker[n] >= 64)
+ return false;
+ }
+ return true;
+}
+
+void mp_chmap_reorder_to_lavc(struct mp_chmap *map)
+{
+ if (!mp_chmap_is_valid(map))
+ return;
+ uint64_t mask = mp_chmap_to_lavc_unchecked(map);
+ mp_chmap_from_lavc(map, mask);
+}
+
+// Get reordering array for from->to reordering. from->to must have the same set
+// of speakers (i.e. same number and speaker IDs, just different order). Then,
+// for each speaker n, dst[n] will be set such that:
+// to->speaker[dst[n]] = from->speaker[n]
+// (dst[n] gives the source channel for destination channel n)
+void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
+ const struct mp_chmap *to)
+{
+ assert(from->num == to->num);
+ if (mp_chmap_is_unknown(from) || mp_chmap_is_unknown(to)) {
+ for (int n = 0; n < from->num; n++)
+ dst[n] = n;
+ return;
+ }
+ // Same set of speakers required
+ assert(mp_chmap_equals_reordered(from, to));
+ for (int n = 0; n < from->num; n++) {
+ int src = from->speaker[n];
+ dst[n] = -1;
+ for (int i = 0; i < to->num; i++) {
+ if (src == to->speaker[i]) {
+ dst[n] = i;
+ break;
+ }
+ }
+ assert(dst[n] != -1);
+ }
+ for (int n = 0; n < from->num; n++)
+ assert(to->speaker[dst[n]] == from->speaker[n]);
+}
+
+// Returns something like "fl-fr-fc". If there's a standard layout in lavc
+// order, return that, e.g. "3.0" instead of "fl-fr-fc".
+// Unassigned but valid speakers get names like "sp28".
+char *mp_chmap_to_str(const struct mp_chmap *src)
+{
+ char *res = talloc_strdup(NULL, "");
+
+ if (mp_chmap_is_unknown(src))
+ return talloc_asprintf_append_buffer(res, "unknown%d", src->num);
+
+ for (int n = 0; n < src->num; n++) {
+ int sp = src->speaker[n];
+ const char *s = sp < MP_SPEAKER_ID_COUNT ? speaker_names[sp][0] : NULL;
+ char buf[10];
+ if (!s) {
+ snprintf(buf, sizeof(buf), "sp%d", sp);
+ s = buf;
+ }
+ res = talloc_asprintf_append_buffer(res, "%s%s", n > 0 ? "-" : "", s);
+ }
+
+ // To standard layout name
+ for (int n = 0; std_layout_names[n][0]; n++) {
+ if (res && strcmp(res, std_layout_names[n][1]) == 0) {
+ talloc_free(res);
+ res = talloc_strdup(NULL, std_layout_names[n][0]);
+ break;
+ }
+ }
+
+ return res;
+}
+
+// If src can be parsed as channel map (as produced by mp_chmap_to_str()),
+// return true and set *dst. Otherwise, return false and don't change *dst.
+// Note: call mp_chmap_is_valid() to test whether the returned map is valid
+// the map could be empty, or contain multiply mapped channels
+bool mp_chmap_from_str(struct mp_chmap *dst, bstr src)
+{
+ // Single number corresponds to mp_chmap_from_channels()
+ if (src.len > 0) {
+ bstr t = src;
+ bool unknown = bstr_eatstart0(&t, "unknown");
+ bstr rest;
+ long long count = bstrtoll(t, &rest, 10);
+ if (rest.len == 0) {
+ struct mp_chmap res;
+ if (unknown) {
+ mp_chmap_set_unknown(&res, count);
+ } else {
+ mp_chmap_from_channels(&res, count);
+ }
+ if (mp_chmap_is_valid(&res)) {
+ *dst = res;
+ return true;
+ }
+ }
+ }
+
+ // From standard layout name
+ for (int n = 0; std_layout_names[n][0]; n++) {
+ if (bstr_equals0(src, std_layout_names[n][0])) {
+ src = bstr0(std_layout_names[n][1]);
+ break;
+ }
+ }
+
+ // Explicit speaker list (separated by "-")
+ struct mp_chmap res = {0};
+ while (src.len) {
+ bstr s;
+ bstr_split_tok(src, "-", &s, &src);
+ int speaker = -1;
+ for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
+ const char *name = speaker_names[n][0];
+ if (name && bstr_equals0(s, name)) {
+ speaker = n;
+ break;
+ }
+ }
+ if (speaker < 0) {
+ if (bstr_eatstart0(&s, "sp")) {
+ long long sp = bstrtoll(s, &s, 0);
+ if (s.len == 0 && sp >= 0 && sp < MP_SPEAKER_ID_COUNT)
+ speaker = sp;
+ }
+ if (speaker < 0)
+ return false;
+ }
+ if (res.num >= MP_NUM_CHANNELS)
+ return false;
+ res.speaker[res.num] = speaker;
+ res.num++;
+ }
+
+ *dst = res;
+ return true;
+}
+
+void mp_chmap_print_help(int msgt, int msgl)
+{
+ mp_msg(msgt, msgl, "Speakers:\n");
+ for (int n = 0; n < MP_SPEAKER_ID_COUNT; n++) {
+ if (speaker_names[n][0])
+ mp_msg(msgt, msgl, " %-16s (%s)\n",
+ speaker_names[n][0], speaker_names[n][1]);
+ }
+ mp_msg(msgt, msgl, "Standard layouts:\n");
+ for (int n = 0; std_layout_names[n][0]; n++) {
+ mp_msg(msgt, msgl, " %-16s (%s)\n",
+ std_layout_names[n][0], std_layout_names[n][1]);
+ }
+ for (int n = 0; n < MP_NUM_CHANNELS; n++)
+ mp_msg(msgt, msgl, " unknown%d\n", n);
+}
diff --git a/audio/chmap.h b/audio/chmap.h
new file mode 100644
index 0000000000..1848c86efd
--- /dev/null
+++ b/audio/chmap.h
@@ -0,0 +1,132 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MP_CHMAP_H
+#define MP_CHMAP_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include "core/bstr.h"
+
+#define MP_NUM_CHANNELS 8
+
+// Speaker a channel can be assigned to.
+// This corresponds to WAVEFORMATEXTENSIBLE channel mask bit indexes.
+// E.g. channel_mask = (1 << MP_SPEAKER_ID_FL) | ...
+enum {
+ // Official WAVEFORMATEXTENSIBLE (shortened names)
+ MP_SPEAKER_ID_FL = 0, // FRONT_LEFT
+ MP_SPEAKER_ID_FR, // FRONT_RIGHT
+ MP_SPEAKER_ID_FC, // FRONT_CENTER
+ MP_SPEAKER_ID_LFE, // LOW_FREQUENCY
+ MP_SPEAKER_ID_BL, // BACK_LEFT
+ MP_SPEAKER_ID_BR, // BACK_RIGHT
+ MP_SPEAKER_ID_FLC, // FRONT_LEFT_OF_CENTER
+ MP_SPEAKER_ID_FRC, // FRONT_RIGHT_OF_CENTER
+ MP_SPEAKER_ID_BC, // BACK_CENTER
+ MP_SPEAKER_ID_SL, // SIDE_LEFT
+ MP_SPEAKER_ID_SR, // SIDE_RIGHT
+ MP_SPEAKER_ID_TC, // TOP_CENTER
+ MP_SPEAKER_ID_TFL, // TOP_FRONT_LEFT
+ MP_SPEAKER_ID_TFC, // TOP_FRONT_CENTER
+ MP_SPEAKER_ID_TFR, // TOP_FRONT_RIGHT
+ MP_SPEAKER_ID_TBL, // TOP_BACK_LEFT
+ MP_SPEAKER_ID_TBC, // TOP_BACK_CENTER
+ MP_SPEAKER_ID_TBR, // TOP_BACK_RIGHT
+ // Inofficial/libav* extensions
+ MP_SPEAKER_ID_DL = 29, // STEREO_LEFT (stereo downmix special speakers)
+ MP_SPEAKER_ID_DR, // STEREO_RIGHT
+ MP_SPEAKER_ID_WL, // WIDE_LEFT
+ MP_SPEAKER_ID_WR, // WIDE_RIGHT
+ MP_SPEAKER_ID_SDL, // SURROUND_DIRECT_LEFT
+ MP_SPEAKER_ID_SDR, // SURROUND_DIRECT_RIGHT
+ MP_SPEAKER_ID_LFE2, // LOW_FREQUENCY_2
+
+ // Special mpv-specific speaker entries reserved for channels which have no
+ // known meaning.
+ MP_SPEAKER_ID_UNKNOWN0 = 64,
+ MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1,
+
+ // Including the unassigned IDs in between. This is not a valid ID anymore.
+ MP_SPEAKER_ID_COUNT,
+};
+
+struct mp_chmap {
+ uint8_t num; // number of channels
+ // Given a channel n, speaker[n] is the speaker ID driven by that channel.
+ // Entries after speaker[num - 1] are undefined.
+ uint8_t speaker[MP_NUM_CHANNELS];
+};
+
+#define MP_SP(speaker) MP_SPEAKER_ID_ ## speaker
+
+#define MP_CHMAP2(a, b) \
+ {2, {MP_SP(a), MP_SP(b)}}
+#define MP_CHMAP3(a, b, c) \
+ {3, {MP_SP(a), MP_SP(b), MP_SP(c)}}
+#define MP_CHMAP4(a, b, c, d) \
+ {4, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d)}}
+#define MP_CHMAP5(a, b, c, d, e) \
+ {5, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e)}}
+#define MP_CHMAP6(a, b, c, d, e, f) \
+ {6, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f)}}
+#define MP_CHMAP7(a, b, c, d, e, f, g) \
+ {7, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g)}}
+#define MP_CHMAP8(a, b, c, d, e, f, g, h) \
+ {8, {MP_SP(a), MP_SP(b), MP_SP(c), MP_SP(d), MP_SP(e), MP_SP(f), MP_SP(g), MP_SP(h)}}
+
+#define MP_CHMAP_INIT_MONO {1, {MP_SPEAKER_ID_FC}}
+#define MP_CHMAP_INIT_STEREO MP_CHMAP2(FL, FR)
+
+bool mp_chmap_is_valid(const struct mp_chmap *src);
+bool mp_chmap_is_empty(const struct mp_chmap *src);
+bool mp_chmap_is_unknown(const struct mp_chmap *src);
+bool mp_chmap_equals(const struct mp_chmap *a, const struct mp_chmap *b);
+bool mp_chmap_equals_reordered(const struct mp_chmap *a, const struct mp_chmap *b);
+bool mp_chmap_is_compatible(const struct mp_chmap *a, const struct mp_chmap *b);
+bool mp_chmap_is_stereo(const struct mp_chmap *src);
+
+void mp_chmap_reorder_norm(struct mp_chmap *map);
+
+void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels);
+void mp_chmap_set_unknown(struct mp_chmap *dst, int num_channels);
+void mp_chmap_from_channels_alsa(struct mp_chmap *dst, int num_channels);
+
+void mp_chmap_remove_useless_channels(struct mp_chmap *map,
+ const struct mp_chmap *requested);
+
+uint64_t mp_chmap_to_lavc(const struct mp_chmap *src);
+uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src);
+void mp_chmap_from_lavc(struct mp_chmap *dst, uint64_t src);
+
+bool mp_chmap_is_lavc(const struct mp_chmap *src);
+void mp_chmap_reorder_to_lavc(struct mp_chmap *map);
+
+void mp_chmap_get_reorder(int dst[MP_NUM_CHANNELS], const struct mp_chmap *from,
+ const struct mp_chmap *to);
+
+char *mp_chmap_to_str(const struct mp_chmap *src);
+bool mp_chmap_from_str(struct mp_chmap *dst, bstr src);
+void mp_chmap_print_help(int msgt, int msgl);
+
+// Use these to avoid chaos in case lavc's definition should diverge from MS.
+#define mp_chmap_to_waveext mp_chmap_to_lavc
+#define mp_chmap_from_waveext mp_chmap_from_lavc
+#define mp_chmap_is_waveext mp_chmap_is_lavc
+#define mp_chmap_reorder_to_waveext mp_chmap_reorder_to_lavc
+
+#endif
diff --git a/audio/chmap_sel.c b/audio/chmap_sel.c
new file mode 100644
index 0000000000..8e5be5c86e
--- /dev/null
+++ b/audio/chmap_sel.c
@@ -0,0 +1,210 @@
+/*
+ * 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 "chmap_sel.h"
+
+// 5.1 and 5.1(side) are practically the same. It doesn't make much sense to
+// reject either of them.
+static const int replaceable_speakers[][2] = {
+ {MP_SPEAKER_ID_SL, MP_SPEAKER_ID_BL},
+ {MP_SPEAKER_ID_SR, MP_SPEAKER_ID_BR},
+ {-1},
+};
+
+// list[] contains a list of speaker pairs, with each pair indicating how
+// a speaker can be swapped for another speaker. Try to replace speakers from
+// the left of the list with the ones on the right, or the other way around.
+static bool replace_speakers(struct mp_chmap *map, const int list[][2])
+{
+ if (!mp_chmap_is_valid(map))
+ return false;
+ for (int dir = 0; dir < 2; dir++) {
+ int from = dir ? 0 : 1;
+ int to = dir ? 1 : 0;
+ bool replaced = false;
+ struct mp_chmap t = *map;
+ for (int n = 0; n < t.num; n++) {
+ for (int i = 0; list[i][0] != -1; i++) {
+ if (t.speaker[n] == list[i][from]) {
+ t.speaker[n] = list[i][to];
+ replaced = true;
+ break;
+ }
+ }
+ }
+ if (replaced && mp_chmap_is_valid(&t)) {
+ *map = t;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Allow all channel layouts that can be expressed with mp_chmap.
+// (By default, all layouts are rejected.)
+void mp_chmap_sel_add_any(struct mp_chmap_sel *s)
+{
+ s->allow_any = true;
+}
+
+// Allow all waveext formats, and force waveext channel order.
+void mp_chmap_sel_add_waveext(struct mp_chmap_sel *s)
+{
+ s->allow_waveext = true;
+}
+
+void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
+{
+ for (int n = 0; n < MP_NUM_CHANNELS; n++) {
+ struct mp_chmap t;
+ mp_chmap_from_channels_alsa(&t, n);
+ if (t.num)
+ mp_chmap_sel_add_map(s, &t);
+ }
+}
+
+#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
+
+// Add a channel map that should be allowed.
+void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map)
+{
+ assert(s->num_chmaps < ARRAY_LEN(s->chmaps));
+ if (mp_chmap_is_valid(map))
+ s->chmaps[s->num_chmaps++] = *map;
+}
+
+// Allow all waveext formats in default order.
+void mp_chmap_sel_add_waveext_def(struct mp_chmap_sel *s)
+{
+ for (int n = 1; n < MP_NUM_CHANNELS; n++) {
+ struct mp_chmap map;
+ mp_chmap_from_channels(&map, n);
+ mp_chmap_sel_add_map(s, &map);
+ }
+}
+
+// Whitelist a speaker (MP_SPEAKER_ID_...). All layouts that contain whitelisted
+// speakers are allowed.
+void mp_chmap_sel_add_speaker(struct mp_chmap_sel *s, int id)
+{
+ assert(id >= 0 && id < MP_SPEAKER_ID_COUNT);
+ s->speakers[id] = true;
+}
+
+static bool test_speakers(const struct mp_chmap_sel *s, struct mp_chmap *map)
+{
+ for (int n = 0; n < map->num; n++) {
+ if (!s->speakers[map->speaker[n]])
+ return false;
+ }
+ return true;
+}
+
+static bool test_maps(const struct mp_chmap_sel *s, struct mp_chmap *map)
+{
+ for (int n = 0; n < s->num_chmaps; n++) {
+ if (mp_chmap_equals_reordered(&s->chmaps[n], map)) {
+ *map = s->chmaps[n];
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool test_waveext(const struct mp_chmap_sel *s, struct mp_chmap *map)
+{
+ if (s->allow_waveext) {
+ struct mp_chmap t = *map;
+ mp_chmap_reorder_to_waveext(&t);
+ if (mp_chmap_is_waveext(&t)) {
+ *map = t;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool test_layout(const struct mp_chmap_sel *s, struct mp_chmap *map)
+{
+ if (!mp_chmap_is_valid(map))
+ return false;
+
+ return s->allow_any || test_waveext(s, map) || test_speakers(s, map) ||
+ test_maps(s, map);
+}
+
+// Determine which channel map to use given a source channel map, and various
+// parameters restricting possible choices. If the map doesn't match, select
+// a fallback and set it.
+// If no matching layout is found, a reordered layout may be returned.
+// If that is not possible, a fallback for up/downmixing may be returned.
+// If no choice is possible, set *map to empty.
+bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map)
+{
+ if (test_layout(s, map))
+ return true;
+ if (mp_chmap_is_unknown(map)) {
+ struct mp_chmap t = {0};
+ if (mp_chmap_sel_get_def(s, &t, map->num) && test_layout(s, &t)) {
+ *map = t;
+ return true;
+ }
+ }
+ // 5.1 <-> 5.1(side)
+ if (replace_speakers(map, replaceable_speakers) && test_layout(s, map))
+ return true;
+ // Fallback to mono/stereo as last resort
+ if (map->num == 1) {
+ *map = (struct mp_chmap) MP_CHMAP_INIT_MONO;
+ } else if (map->num >= 2) {
+ *map = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
+ }
+ if (test_layout(s, map))
+ return true;
+ *map = (struct mp_chmap) {0};
+ return false;
+}
+
+// Set map to a default layout with num channels. Used for audio APIs that
+// return a channel count as part of format negotiation, but give no
+// information about the channel layout.
+// If the channel count is correct, do nothing and leave *map untouched.
+bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
+ int num)
+{
+ if (map->num != num) {
+ *map = (struct mp_chmap) {0};
+ // Set of speakers or waveext might allow it.
+ struct mp_chmap t;
+ mp_chmap_from_channels(&t, num);
+ mp_chmap_reorder_to_waveext(&t);
+ if (test_layout(s, &t)) {
+ *map = t;
+ } else {
+ for (int n = 0; n < s->num_chmaps; n++) {
+ if (s->chmaps[n].num == num) {
+ *map = s->chmaps[n];
+ break;
+ }
+ }
+ }
+ }
+ return map->num > 0;
+}
diff --git a/audio/chmap_sel.h b/audio/chmap_sel.h
new file mode 100644
index 0000000000..c9d75196a5
--- /dev/null
+++ b/audio/chmap_sel.h
@@ -0,0 +1,43 @@
+/*
+ * 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/>.
+ */
+
+#ifndef MP_CHMAP_SEL_H
+#define MP_CHMAP_SEL_H
+
+#include <stdbool.h>
+
+#include "chmap.h"
+
+struct mp_chmap_sel {
+ // should be considered opaque
+ bool allow_any, allow_waveext;
+ bool speakers[MP_SPEAKER_ID_COUNT];
+ struct mp_chmap chmaps[20];
+ int num_chmaps;
+};
+
+void mp_chmap_sel_add_any(struct mp_chmap_sel *s);
+void mp_chmap_sel_add_waveext(struct mp_chmap_sel *s);
+void mp_chmap_sel_add_waveext_def(struct mp_chmap_sel *s);
+void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s);
+void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map);
+void mp_chmap_sel_add_speaker(struct mp_chmap_sel *s, int id);
+bool mp_chmap_sel_adjust(const struct mp_chmap_sel *s, struct mp_chmap *map);
+bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
+ int num);
+
+#endif
diff --git a/audio/decode/ad.h b/audio/decode/ad.h
index 0bbc11ed9b..ff51ecbe35 100644
--- a/audio/decode/ad.h
+++ b/audio/decode/ad.h
@@ -48,10 +48,4 @@ extern const ad_functions_t * const mpcodecs_ad_drivers[];
// fallback if ADCTRL_SKIP not implemented: ds_fill_buffer(sh_audio->ds);
#define ADCTRL_SKIP_FRAME 2 // skip block/frame, called while seeking
-// fallback if ADCTRL_QUERY_FORMAT not implemented: sh_audio->sample_format
-#define ADCTRL_QUERY_FORMAT 3 // test for availabilty of a format
-
-// fallback: use hw mixer in libao
-#define ADCTRL_SET_VOLUME 4 // not used at the moment
-
#endif /* MPLAYER_AD_H */
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index 248df307d7..8abc0a6035 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -32,9 +32,11 @@
#include "core/codecs.h"
#include "core/mp_msg.h"
#include "core/options.h"
+#include "core/av_opts.h"
#include "ad_internal.h"
#include "audio/reorder_ch.h"
+#include "audio/fmt-conversion.h"
#include "compat/mpbswap.h"
#include "compat/libav.h"
@@ -49,6 +51,16 @@ struct priv {
int output_left;
int unitsize;
int previous_data_left; // input demuxer packet data
+ bool force_channel_map;
+};
+
+#define OPT_BASE_STRUCT struct MPOpts
+
+const m_option_t ad_lavc_decode_opts_conf[] = {
+ OPT_FLOATRANGE("ac3drc", ad_lavc_param.ac3drc, 0, 0, 2),
+ OPT_FLAG("downmix", ad_lavc_param.downmix, 0),
+ OPT_STRING("o", ad_lavc_param.avopt, 0),
+ {0}
};
struct pcm_map
@@ -144,17 +156,9 @@ static int preinit(sh_audio_t *sh)
static int setup_format(sh_audio_t *sh_audio,
const AVCodecContext *lavc_context)
{
- int sample_format = sh_audio->sample_format;
- switch (av_get_packed_sample_fmt(lavc_context->sample_fmt)) {
- case AV_SAMPLE_FMT_U8: sample_format = AF_FORMAT_U8; break;
- case AV_SAMPLE_FMT_S16: sample_format = AF_FORMAT_S16_NE; break;
- case AV_SAMPLE_FMT_S32: sample_format = AF_FORMAT_S32_NE; break;
- case AV_SAMPLE_FMT_FLT: sample_format = AF_FORMAT_FLOAT_NE; break;
- default:
- mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n");
- sample_format = AF_FORMAT_UNKNOWN;
- }
-
+ struct priv *priv = sh_audio->context;
+ int sample_format =
+ af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
bool broken_srate = false;
int samplerate = lavc_context->sample_rate;
int container_samplerate = sh_audio->container_out_samplerate;
@@ -166,10 +170,20 @@ static int setup_format(sh_audio_t *sh_audio,
else if (container_samplerate)
samplerate = container_samplerate;
- if (lavc_context->channels != sh_audio->channels ||
+ struct mp_chmap lavc_chmap;
+ mp_chmap_from_lavc(&lavc_chmap, lavc_context->channel_layout);
+ // No channel layout or layout disagrees with channel count
+ if (lavc_chmap.num != lavc_context->channels)
+ mp_chmap_from_channels(&lavc_chmap, lavc_context->channels);
+ if (priv->force_channel_map) {
+ if (lavc_chmap.num == sh_audio->channels.num)
+ lavc_chmap = sh_audio->channels;
+ }
+
+ if (!mp_chmap_equals(&lavc_chmap, &sh_audio->channels) ||
samplerate != sh_audio->samplerate ||
sample_format != sh_audio->sample_format) {
- sh_audio->channels = lavc_context->channels;
+ sh_audio->channels = lavc_chmap;
sh_audio->samplerate = samplerate;
sh_audio->sample_format = sample_format;
sh_audio->samplesize = af_fmt2bits(sh_audio->sample_format) / 8;
@@ -198,41 +212,59 @@ static void set_from_wf(AVCodecContext *avctx, WAVEFORMATEX *wf)
static int init(sh_audio_t *sh_audio, const char *decoder)
{
- struct MPOpts *opts = sh_audio->opts;
+ struct MPOpts *mpopts = sh_audio->opts;
+ struct ad_lavc_param *opts = &mpopts->ad_lavc_param;
AVCodecContext *lavc_context;
AVCodec *lavc_codec;
+ struct priv *ctx = talloc_zero(NULL, struct priv);
+ sh_audio->context = ctx;
+
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
decoder = find_pcm_decoder(tag_map, sh_audio->format,
sh_audio->wf->wBitsPerSample);
} else if (sh_audio->wf && strcmp(decoder, "mp-pcm") == 0) {
decoder = find_pcm_decoder(af_map, sh_audio->format, 0);
+ ctx->force_channel_map = true;
}
lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR,
"Cannot find codec '%s' in libavcodec...\n", decoder);
+ uninit(sh_audio);
return 0;
}
- struct priv *ctx = talloc_zero(NULL, struct priv);
- sh_audio->context = ctx;
lavc_context = avcodec_alloc_context3(lavc_codec);
ctx->avctx = lavc_context;
ctx->avframe = avcodec_alloc_frame();
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
- lavc_context->request_channels = opts->audio_output_channels;
+ if (opts->downmix) {
+ lavc_context->request_channels = mpopts->audio_output_channels.num;
+ lavc_context->request_channel_layout =
+ mp_chmap_to_lavc(&mpopts->audio_output_channels);
+ }
// Always try to set - option only exists for AC3 at the moment
- av_opt_set_double(lavc_context, "drc_scale", opts->drc_level,
+ av_opt_set_double(lavc_context, "drc_scale", opts->ac3drc,
AV_OPT_SEARCH_CHILDREN);
+ if (opts->avopt) {
+ if (parse_avopts(lavc_context, opts->avopt) < 0) {
+ mp_msg(MSGT_DECVIDEO, MSGL_ERR,
+ "ad_lavc: setting AVOptions '%s' failed.\n", opts->avopt);
+ uninit(sh_audio);
+ return 0;
+ }
+ }
+
lavc_context->codec_tag = sh_audio->format;
lavc_context->sample_rate = sh_audio->samplerate;
lavc_context->bit_rate = sh_audio->i_bps * 8;
+ lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
if (sh_audio->wf)
set_from_wf(lavc_context, sh_audio->wf);
@@ -279,13 +311,9 @@ static int init(sh_audio_t *sh_audio, const char *decoder)
if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec;
- switch (av_get_packed_sample_fmt(lavc_context->sample_fmt)) {
- case AV_SAMPLE_FMT_U8:
- case AV_SAMPLE_FMT_S16:
- case AV_SAMPLE_FMT_S32:
- case AV_SAMPLE_FMT_FLT:
- break;
- default:
+ int af_sample_fmt =
+ af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt));
+ if (af_sample_fmt == AF_FORMAT_UNKNOWN) {
uninit(sh_audio);
return 0;
}
@@ -442,13 +470,6 @@ static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
memcpy(buf, priv->output, size);
priv->output += size;
priv->output_left -= size;
- if (avctx->channels >= 5) {
- int samplesize = av_get_bytes_per_sample(avctx->sample_fmt);
- reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
- AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
- avctx->channels,
- size / samplesize, samplesize);
- }
if (len < 0)
len = size;
else
diff --git a/audio/decode/ad_mpg123.c b/audio/decode/ad_mpg123.c
index c17edc9197..8699013acf 100644
--- a/audio/decode/ad_mpg123.c
+++ b/audio/decode/ad_mpg123.c
@@ -358,7 +358,7 @@ static int init(sh_audio_t *sh, const char *decoder)
con->mean_count = 0;
#endif
con->vbr = (finfo.vbr != MPG123_CBR);
- sh->channels = channels;
+ mp_chmap_from_channels(&sh->channels, channels);
sh->samplerate = rate;
/* Without external force, mpg123 will always choose signed encoding,
* and non-16-bit only on builds that don't support it.
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c
index 2101721430..1314110062 100644
--- a/audio/decode/ad_spdif.c
+++ b/audio/decode/ad_spdif.c
@@ -148,19 +148,20 @@ static int init(sh_audio_t *sh, const char *decoder)
}
sh->ds->buffer_pos -= in_size;
+ int num_channels = 0;
switch (lavf_ctx->streams[0]->codec->codec_id) {
case AV_CODEC_ID_AAC:
spdif_ctx->iec61937_packet_size = 16384;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = srate;
- sh->channels = 2;
+ num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_AC3:
spdif_ctx->iec61937_packet_size = 6144;
sh->sample_format = AF_FORMAT_AC3_LE;
sh->samplerate = srate;
- sh->channels = 2;
+ num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_DTS:
@@ -175,13 +176,13 @@ static int init(sh_audio_t *sh, const char *decoder)
spdif_ctx->iec61937_packet_size = 32768;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = 192000; // DTS core require 48000
- sh->channels = 2*4;
+ num_channels = 2*4;
sh->i_bps = bps;
} else {
spdif_ctx->iec61937_packet_size = 32768;
sh->sample_format = AF_FORMAT_AC3_LE;
sh->samplerate = srate;
- sh->channels = 2;
+ num_channels = 2;
sh->i_bps = bps;
}
break;
@@ -189,26 +190,28 @@ static int init(sh_audio_t *sh, const char *decoder)
spdif_ctx->iec61937_packet_size = 24576;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = 192000;
- sh->channels = 2;
+ num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_MP3:
spdif_ctx->iec61937_packet_size = 4608;
sh->sample_format = AF_FORMAT_MPEG2;
sh->samplerate = srate;
- sh->channels = 2;
+ num_channels = 2;
sh->i_bps = bps;
break;
case AV_CODEC_ID_TRUEHD:
spdif_ctx->iec61937_packet_size = 61440;
sh->sample_format = AF_FORMAT_IEC61937_LE;
sh->samplerate = 192000;
- sh->channels = 8;
+ num_channels = 8;
sh->i_bps = bps;
break;
default:
break;
}
+ if (num_channels)
+ mp_chmap_from_channels(&sh->channels, num_channels);
return 1;
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index cac33a9a31..dc461b81e3 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -41,22 +41,14 @@
int fakemono = 0;
-struct af_cfg af_cfg = {1, NULL}; // Configuration for audio filters
+struct af_cfg af_cfg = {0}; // Configuration for audio filters
static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
{
assert(!sh_audio->initialized);
resync_audio_stream(sh_audio);
- sh_audio->samplesize = 2;
- sh_audio->sample_format = AF_FORMAT_S16_NE;
- if ((af_cfg.force & AF_INIT_FORMAT_MASK) == AF_INIT_FLOAT) {
- int fmt = AF_FORMAT_FLOAT_NE;
- if (sh_audio->ad_driver->control(sh_audio, ADCTRL_QUERY_FORMAT,
- &fmt) == CONTROL_TRUE) {
- sh_audio->sample_format = fmt;
- sh_audio->samplesize = 4;
- }
- }
+ sh_audio->samplesize = 4;
+ sh_audio->sample_format = AF_FORMAT_FLOAT_NE;
sh_audio->audio_out_minsize = 8192; // default, preinit() may change it
if (!sh_audio->ad_driver->preinit(sh_audio)) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder preinit failed.\n");
@@ -94,7 +86,7 @@ static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
sh_audio->initialized = 1;
- if (!sh_audio->channels || !sh_audio->samplerate) {
+ if (mp_chmap_is_empty(&sh_audio->channels) || !sh_audio->samplerate) {
mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder did not specify "
"audio format!\n");
uninit_audio(sh_audio); // free buffers
@@ -102,7 +94,7 @@ static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder)
}
if (!sh_audio->o_bps)
- sh_audio->o_bps = sh_audio->channels * sh_audio->samplerate
+ sh_audio->o_bps = sh_audio->channels.num * sh_audio->samplerate
* sh_audio->samplesize;
return 1;
}
@@ -168,14 +160,14 @@ int init_best_audio_codec(sh_audio_t *sh_audio, char *audio_decoders)
sh_audio->gsh->decoder_desc);
mp_msg(MSGT_DECAUDIO, MSGL_V,
"AUDIO: %d Hz, %d ch, %s, %3.1f kbit/%3.2f%% (ratio: %d->%d)\n",
- sh_audio->samplerate, sh_audio->channels,
+ sh_audio->samplerate, sh_audio->channels.num,
af_fmt2str_short(sh_audio->sample_format),
sh_audio->i_bps * 8 * 0.001,
((float) sh_audio->i_bps / sh_audio->o_bps) * 100.0,
sh_audio->i_bps, sh_audio->o_bps);
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
"ID_AUDIO_BITRATE=%d\nID_AUDIO_RATE=%d\n" "ID_AUDIO_NCH=%d\n",
- sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels);
+ sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels.num);
} else {
mp_msg(MSGT_DECAUDIO, MSGL_ERR,
"Failed to initialize an audio decoder for codec '%s'.\n",
@@ -191,7 +183,7 @@ void uninit_audio(sh_audio_t *sh_audio)
if (sh_audio->afilter) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "Uninit audio filters...\n");
af_uninit(sh_audio->afilter);
- free(sh_audio->afilter);
+ af_destroy(sh_audio->afilter);
sh_audio->afilter = NULL;
}
if (sh_audio->initialized) {
@@ -207,43 +199,41 @@ void uninit_audio(sh_audio_t *sh_audio)
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
- int *out_samplerate, int *out_channels, int *out_format)
+ int *out_samplerate, struct mp_chmap *out_channels,
+ int *out_format)
{
struct af_stream *afs = sh_audio->afilter;
- if (!afs) {
- afs = calloc(1, sizeof(struct af_stream));
- afs->opts = sh_audio->opts;
- }
+ if (!afs)
+ afs = af_new(sh_audio->opts);
// input format: same as codec's output format:
- afs->input.rate = in_samplerate;
- afs->input.nch = sh_audio->channels;
- afs->input.format = sh_audio->sample_format;
- af_fix_parameters(&(afs->input));
+ afs->input.rate = in_samplerate;
+ mp_audio_set_channels(&afs->input, &sh_audio->channels);
+ mp_audio_set_format(&afs->input, sh_audio->sample_format);
// output format: same as ao driver's input format (if missing, fallback to input)
- afs->output.rate = *out_samplerate;
- afs->output.nch = *out_channels;
- afs->output.format = *out_format;
- af_fix_parameters(&(afs->output));
+ afs->output.rate = *out_samplerate;
+ mp_audio_set_channels(&afs->output, out_channels);
+ mp_audio_set_format(&afs->output, *out_format);
// filter config:
memcpy(&afs->cfg, &af_cfg, sizeof(struct af_cfg));
+ char *s_from = mp_audio_config_to_str(&afs->input);
+ char *s_to = mp_audio_config_to_str(&afs->output);
mp_tmsg(MSGT_DECAUDIO, MSGL_V,
- "Building audio filter chain for %dHz/%dch/%s -> %dHz/%dch/%s...\n",
- afs->input.rate, afs->input.nch,
- af_fmt2str_short(afs->input.format), afs->output.rate,
- afs->output.nch, af_fmt2str_short(afs->output.format));
+ "Building audio filter chain for %s -> %s...\n", s_from, s_to);
+ talloc_free(s_from);
+ talloc_free(s_to);
// let's autoprobe it!
if (0 != af_init(afs)) {
sh_audio->afilter = NULL;
- free(afs);
+ af_destroy(afs);
return 0; // failed :(
}
*out_samplerate = afs->output.rate;
- *out_channels = afs->output.nch;
+ *out_channels = afs->output.channels;
*out_format = afs->output.format;
// ok!
@@ -270,7 +260,7 @@ static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
// Decode more bytes if needed
int old_samplerate = sh->samplerate;
- int old_channels = sh->channels;
+ struct mp_chmap old_channels = sh->channels;
int old_sample_format = sh->sample_format;
while (sh->a_buffer_len < len) {
unsigned char *buf = sh->a_buffer + sh->a_buffer_len;
@@ -278,7 +268,7 @@ static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
int maxlen = sh->a_buffer_size - sh->a_buffer_len;
int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen);
int format_change = sh->samplerate != old_samplerate
- || sh->channels != old_channels
+ || !mp_chmap_equals(&sh->channels, &old_channels)
|| sh->sample_format != old_sample_format;
if (ret <= 0 || format_change) {
error = format_change ? -2 : -1;
@@ -294,10 +284,10 @@ static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len)
.audio = sh->a_buffer,
.len = len,
.rate = sh->samplerate,
- .nch = sh->channels,
- .format = sh->sample_format
};
- af_fix_parameters(&filter_input);
+ mp_audio_set_format(&filter_input, sh->sample_format);
+ mp_audio_set_channels(&filter_input, &sh->channels);
+
struct mp_audio *filter_output = af_play(sh->afilter, &filter_input);
if (!filter_output)
return -1;
@@ -325,7 +315,7 @@ int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen)
// Indicates that a filter seems to be buffering large amounts of data
int huge_filter_buffer = 0;
// Decoded audio must be cut at boundaries of this many bytes
- int unitsize = sh_audio->channels * sh_audio->samplesize * 16;
+ int unitsize = sh_audio->channels.num * sh_audio->samplesize * 16;
/* Filter output size will be about filter_multiplier times input size.
* If some filter buffers audio in big blocks this might only hold
diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h
index 9fa6aad8dd..b46f4282fb 100644
--- a/audio/decode/dec_audio.h
+++ b/audio/decode/dec_audio.h
@@ -19,6 +19,7 @@
#ifndef MPLAYER_DEC_AUDIO_H
#define MPLAYER_DEC_AUDIO_H
+#include "audio/chmap.h"
#include "demux/stheader.h"
struct bstr;
@@ -33,6 +34,7 @@ void skip_audio_frame(sh_audio_t *sh_audio);
void uninit_audio(sh_audio_t *sh_audio);
int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
- int *out_samplerate, int *out_channels, int *out_format);
+ int *out_samplerate, struct mp_chmap *out_channels,
+ int *out_format);
#endif /* MPLAYER_DEC_AUDIO_H */
diff --git a/audio/filter/af.c b/audio/filter/af.c
index ad43e5fca7..137e7cc407 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include "af.h"
@@ -28,6 +29,7 @@ extern struct af_info af_info_dummy;
extern struct af_info af_info_delay;
extern struct af_info af_info_channels;
extern struct af_info af_info_format;
+extern struct af_info af_info_force;
extern struct af_info af_info_volume;
extern struct af_info af_info_equalizer;
extern struct af_info af_info_pan;
@@ -47,232 +49,389 @@ extern struct af_info af_info_karaoke;
extern struct af_info af_info_scaletempo;
extern struct af_info af_info_bs2b;
-static struct af_info* filter_list[]={
- &af_info_dummy,
- &af_info_delay,
- &af_info_channels,
- &af_info_format,
- &af_info_volume,
- &af_info_equalizer,
- &af_info_pan,
- &af_info_surround,
- &af_info_sub,
+static struct af_info* filter_list[] = {
+ &af_info_dummy,
+ &af_info_delay,
+ &af_info_channels,
+ &af_info_force,
+ &af_info_volume,
+ &af_info_equalizer,
+ &af_info_pan,
+ &af_info_surround,
+ &af_info_sub,
#ifdef HAVE_SYS_MMAN_H
- &af_info_export,
+ &af_info_export,
#endif
- &af_info_drc,
- &af_info_extrastereo,
- &af_info_lavcac3enc,
- &af_info_lavrresample,
- &af_info_sweep,
- &af_info_hrtf,
+ &af_info_drc,
+ &af_info_extrastereo,
+ &af_info_lavcac3enc,
+ &af_info_lavrresample,
+ &af_info_sweep,
+ &af_info_hrtf,
#ifdef CONFIG_LADSPA
- &af_info_ladspa,
+ &af_info_ladspa,
#endif
- &af_info_center,
- &af_info_sinesuppress,
- &af_info_karaoke,
- &af_info_scaletempo,
+ &af_info_center,
+ &af_info_sinesuppress,
+ &af_info_karaoke,
+ &af_info_scaletempo,
#ifdef CONFIG_LIBBS2B
- &af_info_bs2b,
+ &af_info_bs2b,
#endif
- NULL
+ // Must come last, because it's the fallback format conversion filter
+ &af_info_format,
+ NULL
};
-// CPU speed
-int* af_cpu_speed = NULL;
+static bool af_config_equals(struct mp_audio *a, struct mp_audio *b)
+{
+ return a->format == b->format
+ && mp_chmap_equals(&a->channels, &b->channels)
+ && a->rate == b->rate;
+}
+
+static void af_copy_unset_fields(struct mp_audio *dst, struct mp_audio *src)
+{
+ if (dst->format == AF_FORMAT_UNKNOWN)
+ mp_audio_set_format(dst, src->format);
+ if (dst->nch == 0)
+ mp_audio_set_channels(dst, &src->channels);
+ if (dst->rate == 0)
+ dst->rate = src->rate;
+}
+
+static int input_control(struct af_instance* af, int cmd, void* arg)
+{
+ switch (cmd) {
+ case AF_CONTROL_REINIT:
+ assert(arg == &((struct af_stream *)af->setup)->input);
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+static int output_control(struct af_instance* af, int cmd, void* arg)
+{
+ struct af_stream *s = af->setup;
+ struct mp_audio *output = &s->output;
+ struct mp_audio *filter_output = &s->filter_output;
+
+ switch (cmd) {
+ case AF_CONTROL_REINIT: {
+ struct mp_audio *in = arg;
+ struct mp_audio orig_in = *in;
+
+ *filter_output = *output;
+ af_copy_unset_fields(filter_output, in);
+ *in = *filter_output;
+ return af_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+static struct mp_audio *dummy_play(struct af_instance* af, struct mp_audio* data)
+{
+ return data;
+}
/* Find a filter in the static list of filters using it's name. This
function is used internally */
-static struct af_info* af_find(char*name)
+static struct af_info *af_find(char *name)
{
- int i=0;
- while(filter_list[i]){
- if(!strcmp(filter_list[i]->name,name))
- return filter_list[i];
- i++;
- }
- mp_msg(MSGT_AFILTER, MSGL_ERR, "Couldn't find audio filter '%s'\n",name);
- return NULL;
+ int i = 0;
+ while (filter_list[i]) {
+ if (!strcmp(filter_list[i]->name, name))
+ return filter_list[i];
+ i++;
+ }
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "Couldn't find audio filter '%s'\n", name);
+ return NULL;
}
/* Find filter in the dynamic filter list using it's name This
function is used for finding already initialized filters */
-struct af_instance* af_get(struct af_stream* s, char* name)
-{
- struct af_instance* af=s->first;
- // Find the filter
- while(af != NULL){
- if(!strcmp(af->info->name,name))
- return af;
- af=af->next;
- }
- return NULL;
-}
-
-/*/ Function for creating a new filter of type name. The name may
- contain the commandline parameters for the filter */
-static struct af_instance* af_create(struct af_stream* s, const char* name_with_cmd)
-{
- char* name = strdup(name_with_cmd);
- char* cmdline = name;
-
- // Allocate space for the new filter and reset all pointers
- struct af_instance* new=malloc(sizeof(struct af_instance));
- if (!name || !new) {
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n");
- goto err_out;
- }
- memset(new,0,sizeof(struct af_instance));
-
- // Check for commandline parameters
- char *skip = strstr(cmdline, "=");
- if (skip) {
- *skip = '\0'; // for name
- cmdline = skip + 1;
- } else {
- cmdline = NULL;
- }
-
- // Find filter from name
- if(NULL == (new->info=af_find(name)))
- goto err_out;
-
- /* Make sure that the filter is not already in the list if it is
- non-reentrant */
- if(new->info->flags & AF_FLAGS_NOT_REENTRANT){
- if(af_get(s,name)){
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one instance of"
- " the filter '%s' in each stream\n",name);
- goto err_out;
+struct af_instance *af_get(struct af_stream *s, char *name)
+{
+ struct af_instance *af = s->first;
+ // Find the filter
+ while (af != NULL) {
+ if (!strcmp(af->info->name, name))
+ return af;
+ af = af->next;
}
- }
+ return NULL;
+}
- mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n",name);
+/* Function for creating a new filter of type name.The name may
+contain the commandline parameters for the filter */
+static struct af_instance *af_create(struct af_stream *s,
+ const char *name_with_cmd)
+{
+ char *name = strdup(name_with_cmd);
+ char *cmdline = name;
- // Initialize the new filter
- if(AF_OK == new->info->open(new) &&
- AF_ERROR < new->control(new,AF_CONTROL_POST_CREATE,&s->cfg)){
- if(cmdline){
- if(AF_ERROR>=new->control(new,AF_CONTROL_COMMAND_LINE,cmdline))
+ // Allocate space for the new filter and reset all pointers
+ struct af_instance *new = malloc(sizeof(struct af_instance));
+ if (!name || !new) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n");
goto err_out;
}
- free(name);
- return new;
- }
+ memset(new, 0, sizeof(struct af_instance));
+
+ // Check for commandline parameters
+ char *skip = strstr(cmdline, "=");
+ if (skip) {
+ *skip = '\0'; // for name
+ cmdline = skip + 1;
+ } else {
+ cmdline = NULL;
+ }
+
+ // Find filter from name
+ if (NULL == (new->info = af_find(name)))
+ goto err_out;
+
+ /* Make sure that the filter is not already in the list if it is
+ non-reentrant */
+ if (new->info->flags & AF_FLAGS_NOT_REENTRANT) {
+ if (af_get(s, name)) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one "
+ "instance of the filter '%s' in each stream\n", name);
+ goto err_out;
+ }
+ }
+
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n", name);
+
+ // Initialize the new filter
+ if (AF_OK == new->info->open(new)) {
+ if (cmdline) {
+ if (AF_ERROR >= new->control(new, AF_CONTROL_COMMAND_LINE, cmdline))
+ goto err_out;
+ }
+ free(name);
+ return new;
+ }
err_out:
- free(new);
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Couldn't create or open audio filter '%s'\n",
- name);
- free(name);
- return NULL;
+ free(new);
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[libaf] Couldn't create or open audio filter '%s'\n", name);
+ free(name);
+ return NULL;
}
/* Create and insert a new filter of type name before the filter in the
argument. This function can be called during runtime, the return
value is the new filter */
-static struct af_instance* af_prepend(struct af_stream* s, struct af_instance* af, const char* name)
+static struct af_instance *af_prepend(struct af_stream *s,
+ struct af_instance *af,
+ const char *name)
{
- // Create the new filter and make sure it is OK
- struct af_instance* new=af_create(s,name);
- if(!new)
- return NULL;
- // Update pointers
- new->next=af;
- if(af){
- new->prev=af->prev;
- af->prev=new;
- }
- else
- s->last=new;
- if(new->prev)
- new->prev->next=new;
- else
- s->first=new;
- return new;
+ if (!af)
+ af = s->last;
+ if (af == s->first)
+ af = s->first->next;
+ // Create the new filter and make sure it is OK
+ struct af_instance *new = af_create(s, name);
+ if (!new)
+ return NULL;
+ // Update pointers
+ new->next = af;
+ new->prev = af->prev;
+ af->prev = new;
+ new->prev->next = new;
+ return new;
}
/* Create and insert a new filter of type name after the filter in the
argument. This function can be called during runtime, the return
value is the new filter */
-static struct af_instance* af_append(struct af_stream* s, struct af_instance* af, const char* name)
+static struct af_instance *af_append(struct af_stream *s,
+ struct af_instance *af,
+ const char *name)
{
- // Create the new filter and make sure it is OK
- struct af_instance* new=af_create(s,name);
- if(!new)
- return NULL;
- // Update pointers
- new->prev=af;
- if(af){
- new->next=af->next;
- af->next=new;
- }
- else
- s->first=new;
- if(new->next)
- new->next->prev=new;
- else
- s->last=new;
- return new;
+ if (!af)
+ af = s->first;
+ if (af == s->last)
+ af = s->last->prev;
+ // Create the new filter and make sure it is OK
+ struct af_instance *new = af_create(s, name);
+ if (!new)
+ return NULL;
+ // Update pointers
+ new->prev = af;
+ new->next = af->next;
+ af->next = new;
+ new->next->prev = new;
+ return new;
}
// Uninit and remove the filter "af"
-void af_remove(struct af_stream* s, struct af_instance* af)
+void af_remove(struct af_stream *s, struct af_instance *af)
{
- if(!af) return;
+ if (!af)
+ return;
+
+ if (af == s->first || af == s->last)
+ return;
- // Print friendly message
- mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",af->info->name);
+ // Print friendly message
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",
+ af->info->name);
- // Notify filter before changing anything
- af->control(af,AF_CONTROL_PRE_DESTROY,0);
+ // Notify filter before changing anything
+ af->control(af, AF_CONTROL_PRE_DESTROY, 0);
- // Detach pointers
- if(af->prev)
- af->prev->next=af->next;
- else
- s->first=af->next;
- if(af->next)
- af->next->prev=af->prev;
- else
- s->last=af->prev;
+ // Detach pointers
+ af->prev->next = af->next;
+ af->next->prev = af->prev;
- // Uninitialize af and free memory
- af->uninit(af);
- free(af);
+ af->uninit(af);
+ free(af);
}
-static void print_fmt(struct mp_audio *d)
+static void remove_auto_inserted_filters(struct af_stream *s)
{
- if (d) {
- mp_msg(MSGT_AFILTER, MSGL_V, "%dHz/%dch/%s", d->rate, d->nch,
- af_fmt2str_short(d->format));
- } else {
- mp_msg(MSGT_AFILTER, MSGL_V, "(?)");
+repeat:
+ for (struct af_instance *af = s->first; af; af = af->next) {
+ if (af->auto_inserted) {
+ af_remove(s, af);
+ goto repeat;
+ }
}
}
-static void af_print_filter_chain(struct af_stream* s)
+static void af_print_filter_chain(struct af_stream *s, struct af_instance *at,
+ int msg_level)
{
- mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n");
-
- mp_msg(MSGT_AFILTER, MSGL_V, " [in] ");
- print_fmt(&s->input);
- mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+ mp_msg(MSGT_AFILTER, msg_level, "Audio filter chain:\n");
struct af_instance *af = s->first;
while (af) {
- mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name);
- print_fmt(af->data);
- mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+ mp_msg(MSGT_AFILTER, msg_level, " [%s] ", af->info->name);
+ if (af->data) {
+ char *info = mp_audio_config_to_str(af->data);
+ mp_msg(MSGT_AFILTER, msg_level, "%s", info);
+ talloc_free(info);
+ }
+ if (af == at)
+ mp_msg(MSGT_AFILTER, msg_level, " <-");
+ mp_msg(MSGT_AFILTER, msg_level, "\n");
af = af->next;
}
- mp_msg(MSGT_AFILTER, MSGL_V, " [out] ");
- print_fmt(&s->output);
- mp_msg(MSGT_AFILTER, MSGL_V, "\n");
+ mp_msg(MSGT_AFILTER, msg_level, " [ao] ");
+ char *info = mp_audio_config_to_str(&s->output);
+ mp_msg(MSGT_AFILTER, msg_level, "%s\n", info);
+ talloc_free(info);
+}
+
+static int af_count_filters(struct af_stream *s)
+{
+ int count = 0;
+ for (struct af_instance *af = s->first; af; af = af->next)
+ count++;
+ return count;
+}
+
+static const 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 af->name;
+ }
+ return NULL;
+}
+
+static bool af_is_conversion_filter(struct af_instance *af)
+{
+ return af && af->info->test_conversion != NULL;
+}
+
+// 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_FALSE: nothing was changed, format matches
+// else: error
+static int af_fix_format_conversion(struct af_stream *s,
+ struct af_instance **p_af,
+ struct mp_audio in)
+{
+ int rv;
+ struct af_instance *af = *p_af;
+ struct af_instance *prev = af->prev;
+ 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;
+ }
+ const char *filter = af_find_conversion_filter(actual.format, in.format);
+ if (!filter)
+ return AF_ERROR;
+ struct af_instance *new = af_prepend(s, af, filter);
+ if (new == NULL)
+ return AF_ERROR;
+ new->auto_inserted = true;
+ if (AF_OK != (rv = new->control(new, AF_CONTROL_FORMAT_FMT, &in.format)))
+ return rv;
+ *p_af = new;
+ return AF_OK;
+}
+
+// same as af_fix_format_conversion - only wrt. channels
+static int af_fix_channels(struct af_stream *s, struct af_instance **p_af,
+ struct mp_audio in)
+{
+ int rv;
+ struct af_instance *af = *p_af;
+ struct af_instance *prev = af->prev;
+ struct mp_audio actual = *prev->data;
+ if (mp_chmap_equals(&actual.channels, &in.channels))
+ return AF_FALSE;
+ if (prev->control(prev, AF_CONTROL_CHANNELS, &in.channels) == AF_OK) {
+ *p_af = prev;
+ return AF_OK;
+ }
+ const char *filter = "lavrresample";
+ struct af_instance *new = af_prepend(s, af, filter);
+ if (new == NULL)
+ return AF_ERROR;
+ new->auto_inserted = true;
+ if (AF_OK != (rv = new->control(new, AF_CONTROL_CHANNELS, &in.channels)))
+ return rv;
+ *p_af = new;
+ return AF_OK;
+}
+
+static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
+ struct mp_audio in)
+{
+ int rv;
+ struct af_instance *af = *p_af;
+ struct af_instance *prev = af->prev;
+ struct mp_audio actual = *prev->data;
+ if (actual.rate == in.rate)
+ return AF_FALSE;
+ if (prev->control(prev, AF_CONTROL_RESAMPLE_RATE, &in.rate) == AF_OK) {
+ *p_af = prev;
+ return AF_OK;
+ }
+ const char *filter = "lavrresample";
+ struct af_instance *new = af_prepend(s, af, filter);
+ if (new == NULL)
+ return AF_ERROR;
+ new->auto_inserted = true;
+ if (AF_OK != (rv = new->control(new, AF_CONTROL_RESAMPLE_RATE, &in.rate)))
+ return rv;
+ *p_af = new;
+ return AF_OK;
}
// Warning:
@@ -280,186 +439,128 @@ static void af_print_filter_chain(struct af_stream* s)
// state (for example, format filters that were tentatively inserted stay
// inserted).
// In that case, you should always rebuild the filter chain, or abort.
-int af_reinit(struct af_stream* s, struct af_instance* af)
-{
- do{
- struct mp_audio in; // Format of the input to current filter
- int rv=0; // Return value
-
- // Check if there are any filters left in the list
- if(NULL == af){
- if(!(af=af_append(s,s->first,"dummy")))
- return AF_UNKNOWN;
- else
- return AF_ERROR;
+// Also, note that for complete reinit, fixup_output_format() may have to be
+// called after this function.
+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
+ int retry = 0;
+ while (af) {
+ if (retry >= max_retry)
+ goto negotiate_error;
+
+ // Check if this is the first filter
+ struct mp_audio in = *af->prev->data;
+ // Reset just in case...
+ in.audio = NULL;
+ in.len = 0;
+
+ int rv = af->control(af, AF_CONTROL_REINIT, &in);
+ switch (rv) {
+ case AF_OK:
+ af = af->next;
+ break;
+ case AF_FALSE: { // Configuration filter is needed
+ if (af_fix_channels(s, &af, in) == AF_OK) {
+ retry++;
+ continue;
+ }
+ if (af_fix_rate(s, &af, in) == AF_OK) {
+ retry++;
+ continue;
+ }
+ // Do this last, to prevent "format->lavrresample" being added to
+ // the filter chain when output formats not supported by
+ // af_lavrresample are in use.
+ if (af_fix_format_conversion(s, &af, in) == AF_OK) {
+ retry++;
+ continue;
+ }
+ goto negotiate_error;
+ }
+ case AF_DETACH: { // Filter is redundant and wants to be unloaded
+ struct af_instance *aft = af->prev; // never NULL
+ af_remove(s, af);
+ af = aft->next;
+ break;
+ }
+ default:
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not "
+ "work, audio filter '%s' returned error code %i\n",
+ af->info->name, rv);
+ af_print_filter_chain(s, af, MSGL_ERR);
+ return AF_ERROR;
+ }
}
- // Check if this is the first filter
- if(!af->prev)
- memcpy(&in,&(s->input),sizeof(struct mp_audio));
- else
- memcpy(&in,af->prev->data,sizeof(struct mp_audio));
- // Reset just in case...
- in.audio=NULL;
- in.len=0;
-
- rv = af->control(af,AF_CONTROL_REINIT,&in);
- switch(rv){
- case AF_OK:
- af = af->next;
- break;
- case AF_FALSE:{ // Configuration filter is needed
- // Do auto insertion only if force is not specified
- if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
- struct af_instance* new = NULL;
- // Insert channels filter
- if((af->prev?af->prev->data->nch:s->input.nch) != in.nch){
- // Create channels filter
- if(NULL == (new = af_prepend(s,af,"channels")))
- return AF_ERROR;
- // Set number of output channels
- if(AF_OK != (rv = new->control(new,AF_CONTROL_CHANNELS,&in.nch)))
- return rv;
- // Initialize channels filter
- if(!new->prev)
- memcpy(&in,&(s->input),sizeof(struct mp_audio));
- else
- memcpy(&in,new->prev->data,sizeof(struct mp_audio));
- if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
- return rv;
- }
- // Insert format filter
- if((af->prev?af->prev->data->format:s->input.format) != in.format){
- // Create format filter
- if(NULL == (new = af_prepend(s,af,"format")))
- return AF_ERROR;
- // Set output bits per sample
- in.format |= af_bits2fmt(in.bps*8);
- if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_FMT,&in.format)))
- return rv;
- // Initialize format filter
- if(!new->prev)
- memcpy(&in,&(s->input),sizeof(struct mp_audio));
- else
- memcpy(&in,new->prev->data,sizeof(struct mp_audio));
- if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in)))
- return rv;
- }
- if(!new){ // Should _never_ happen
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
- "This error should never occur, please send a bug report.\n");
- return AF_ERROR;
- }
- af=new->next;
- }
- else {
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Automatic filter insertion disabled "
- "but formats do not match. Giving up.\n");
- return AF_ERROR;
- }
- break;
- }
- case AF_DETACH:{ // Filter is redundant and wants to be unloaded
- // Do auto remove only if force is not specified
- if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
- struct af_instance* aft=af->prev;
- af_remove(s,af);
- if(aft)
- af=aft->next;
- else
- af=s->first; // Restart configuration
- }
- break;
- }
- default:
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not work, audio"
- " filter '%s' returned error code %i\n",af->info->name,rv);
- return AF_ERROR;
- }
- }while(af);
+ af_print_filter_chain(s, NULL, MSGL_V);
- af_print_filter_chain(s);
+ return AF_OK;
- return AF_OK;
+negotiate_error:
+ mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. "
+ "This error should never occur, please send a bug report.\n");
+ af_print_filter_chain(s, af, MSGL_ERR);
+ return AF_ERROR;
}
// Uninit and remove all filters
-void af_uninit(struct af_stream* s)
+void af_uninit(struct af_stream *s)
{
- while(s->first)
- af_remove(s,s->first);
+ while (s->first->next && s->first->next != s->last)
+ af_remove(s, s->first->next);
}
-/**
- * Extend the filter chain so we get the required output format at the end.
- * \return AF_ERROR on error, AF_OK if successful.
- */
-static int fixup_output_format(struct af_stream* s)
-{
- struct af_instance* af = NULL;
- // Check number of output channels fix if not OK
- // If needed always inserted last -> easy to screw up other filters
- if(s->output.nch && s->last->data->nch!=s->output.nch){
- if(!strcmp(s->last->info->name,"format"))
- af = af_prepend(s,s->last,"channels");
- else
- af = af_append(s,s->last,"channels");
- // Init the new filter
- if(!af || (AF_OK != af->control(af,AF_CONTROL_CHANNELS,&(s->output.nch))))
- return AF_ERROR;
- if(AF_OK != af_reinit(s,af))
- return AF_ERROR;
- }
-
- // Check output format fix if not OK
- if(s->output.format != AF_FORMAT_UNKNOWN &&
- s->last->data->format != s->output.format){
- if(strcmp(s->last->info->name,"format"))
- af = af_append(s,s->last,"format");
- else
- af = s->last;
- // Init the new filter
- s->output.format |= af_bits2fmt(s->output.bps*8);
- if(!af || (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT,&(s->output.format))))
- return AF_ERROR;
- if(AF_OK != af_reinit(s,af))
- return AF_ERROR;
- }
+struct af_stream *af_new(struct MPOpts *opts)
+{
+ struct af_stream *s = talloc_zero(NULL, struct af_stream);
+ static struct af_info in = { .name = "in" };
+ s->first = talloc(s, struct af_instance);
+ *s->first = (struct af_instance) {
+ .info = &in,
+ .control = input_control,
+ .play = dummy_play,
+ .setup = s,
+ .data = &s->input,
+ .mul = 1.0,
+ };
+ static struct af_info out = { .name = "out" };
+ s->last = talloc(s, struct af_instance);
+ *s->last = (struct af_instance) {
+ .info = &out,
+ .control = output_control,
+ .play = dummy_play,
+ .setup = s,
+ .data = &s->filter_output,
+ .mul = 1.0,
+ };
+ s->first->next = s->last;
+ s->last->prev = s->first;
+ return s;
+}
- // Re init again just in case
- if(AF_OK != af_reinit(s,s->first))
- return AF_ERROR;
-
- if (s->output.format == AF_FORMAT_UNKNOWN)
- s->output.format = s->last->data->format;
- if (!s->output.nch) s->output.nch = s->last->data->nch;
- if (!s->output.rate) s->output.rate = s->last->data->rate;
- if((s->last->data->format != s->output.format) ||
- (s->last->data->nch != s->output.nch) ||
- (s->last->data->rate != s->output.rate)) {
- return AF_ERROR;
- }
- return AF_OK;
+void af_destroy(struct af_stream *s)
+{
+ af_uninit(s);
+ talloc_free(s);
}
-/**
- * Automatic downmix to stereo in case the codec does not implement it.
+/*
+ * Set previously unset fields in s->output to those of the filter chain
+ * output. This is used to make the output format fixed, and even if you insert
+ * new filters or change the input format, the output format won't change.
+ * \return AF_ERROR on error, AF_OK if successful.
*/
-static void af_downmix(struct af_stream* s)
-{
- static const char * const downmix_strs[AF_NCH + 1] = {
- /* FL FR RL RR FC LF AL AR */
- [3] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0.4",
- [4] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0:" "0:0.4",
- [5] = "pan=2:" "0.5:0:" "0:0.5:" "0.2:0:" "0:0.2:" "0.3:0.3",
- [6] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0.1",
- [7] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0:" "0:0.1",
- [8] = "pan=2:" "0.4:0:" "0:0.4:" "0.15:0:" "0:0.15:" "0.25:0.25:" "0.1:0.1:" "0.1:0:" "0:0.1",
- };
- const char *af_pan_str = downmix_strs[s->input.nch];
+static int fixup_output_format(struct af_stream *s)
+{
+ if (AF_OK != af_reinit(s))
+ return AF_ERROR;
- if (af_pan_str)
- af_append(s, s->first, af_pan_str);
+ af_copy_unset_fields(&s->output, &s->filter_output);
+ return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR;
}
/* Initialize the stream "s". This function creates a new filter list
@@ -471,221 +572,176 @@ static void af_downmix(struct af_stream* s)
If one of the prefered output parameters is 0 the one that needs
no conversion is used (i.e. the output format in the last filter).
The return value is 0 if success and -1 if failure */
-int af_init(struct af_stream* s)
-{
- struct MPOpts *opts = s->opts;
- int i=0;
-
- // Sanity check
- if(!s) return -1;
-
- // Precaution in case caller is misbehaving
- s->input.audio = s->output.audio = NULL;
- s->input.len = s->output.len = 0;
-
- // Figure out how fast the machine is
- if(AF_INIT_AUTO == (AF_INIT_TYPE_MASK & s->cfg.force))
- s->cfg.force = (s->cfg.force & ~AF_INIT_TYPE_MASK) | AF_INIT_TYPE;
-
- // Check if this is the first call
- if(!s->first){
- // Append a downmix pan filter at the beginning of the chain if needed
- if (s->input.nch != opts->audio_output_channels
- && opts->audio_output_channels == 2)
- af_downmix(s);
- // Add all filters in the list (if there are any)
- if (s->cfg.list) {
- while(s->cfg.list[i]){
- if(!af_append(s,s->last,s->cfg.list[i++]))
- return -1;
- }
- }
- }
-
- // If we do not have any filters otherwise
- // add dummy to make automatic format conversion work
- if (!s->first && !af_append(s, s->first, "dummy"))
- return -1;
-
- // Init filters
- if(AF_OK != af_reinit(s,s->first))
- return -1;
-
- // make sure the chain is not empty and valid (e.g. because of AF_DETACH)
- if (!s->first)
- if (!af_append(s,s->first,"dummy") || AF_OK != af_reinit(s,s->first))
- return -1;
-
- // Check output format
- if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
- struct af_instance* af = NULL; // New filter
- // Check output frequency if not OK fix with resample
- if(s->output.rate && s->last->data->rate!=s->output.rate){
- // try to find a filter that can change samplrate
- af = af_control_any_rev(s, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
- &(s->output.rate));
- if (!af) {
- char *resampler = "lavrresample";
- if((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW){
- if(!strcmp(s->first->info->name,"format"))
- af = af_append(s,s->first,resampler);
- else
- af = af_prepend(s,s->first,resampler);
- }
- else{
- if(!strcmp(s->last->info->name,"format"))
- af = af_prepend(s,s->last,resampler);
- else
- af = af_append(s,s->last,resampler);
- }
- // Init the new filter
- if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET,
- &(s->output.rate))))
- return -1;
- }
- if(AF_OK != af_reinit(s,af))
- return -1;
+int af_init(struct af_stream *s)
+{
+ int i = 0;
+
+ // Sanity check
+ if (!s)
+ return -1;
+
+ // Precaution in case caller is misbehaving
+ s->input.audio = s->output.audio = NULL;
+ s->input.len = s->output.len = 0;
+
+ // Check if this is the first call
+ if (s->first->next == s->last) {
+ // Add all filters in the list (if there are any)
+ if (s->cfg.list) {
+ while (s->cfg.list[i]) {
+ if (!af_prepend(s, s->last, s->cfg.list[i++]))
+ return -1;
+ }
+ }
}
+
+ remove_auto_inserted_filters(s);
+
+ // Init filters
+ if (AF_OK != af_reinit(s))
+ return -1;
+
if (AF_OK != fixup_output_format(s)) {
- // Something is stuffed audio out will not work
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to setup filter system can not"
- " meet sound-card demands, please send a bug report. \n");
- af_uninit(s);
- return -1;
+ // Something is stuffed audio out will not work
+ mp_msg(
+ MSGT_AFILTER, MSGL_ERR,
+ "[libaf] Unable to setup filter system can not"
+ " meet sound-card demands, please send a bug report. \n");
+ af_uninit(s);
+ return -1;
}
- }
- return 0;
+ return 0;
}
/* Add filter during execution. This function adds the filter "name"
to the stream s. The filter will be inserted somewhere nice in the
list of filters. The return value is a pointer to the new filter,
If the filter couldn't be added the return value is NULL. */
-struct af_instance* af_add(struct af_stream* s, char* name){
- struct af_instance* new;
- // Sanity check
- if(!s || !s->first || !name)
- return NULL;
- // Insert the filter somewhere nice
- if(!strcmp(s->first->info->name,"format"))
- new = af_append(s, s->first, name);
- else
- new = af_prepend(s, s->first, name);
- if(!new)
- return NULL;
-
- // Reinitalize the filter list
- if(AF_OK != af_reinit(s, s->first) ||
- AF_OK != fixup_output_format(s)){
- while (s->first)
- af_remove(s, s->first);
- af_init(s);
- return NULL;
- }
- return new;
+struct af_instance *af_add(struct af_stream *s, char *name)
+{
+ struct af_instance *new;
+ // Sanity check
+ if (!s || !s->first || !name)
+ return NULL;
+ // Insert the filter somewhere nice
+ if (af_is_conversion_filter(s->first->next))
+ new = af_append(s, s->first->next, name);
+ else
+ new = af_prepend(s, s->first->next, name);
+ if (!new)
+ return NULL;
+
+ // Reinitalize the filter list
+ if (AF_OK != af_reinit(s) ||
+ AF_OK != fixup_output_format(s)) {
+ while (s->first)
+ af_remove(s, s->first);
+ af_init(s);
+ return NULL;
+ }
+ return new;
}
// Filter data chunk through the filters in the list
-struct mp_audio* af_play(struct af_stream* s, struct mp_audio* data)
+struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data)
{
- struct af_instance* af=s->first;
- // Iterate through all filters
- do{
- if (data->len <= 0) break;
- data=af->play(af,data);
- af=af->next;
- }while(af && data);
- return data;
+ struct af_instance *af = s->first;
+ // Iterate through all filters
+ do {
+ if (data->len <= 0)
+ break;
+ data = af->play(af, data);
+ af = af->next;
+ } while (af && data);
+ return data;
}
/* Calculate the minimum output buffer size for given input data d
* when using the RESIZE_LOCAL_BUFFER macro. The +t+1 part ensures the
* value is >= len*mul rounded upwards to whole samples even if the
* double 'mul' is inexact. */
-int af_lencalc(double mul, struct mp_audio* d)
+int af_lencalc(double mul, struct mp_audio *d)
{
- int t = d->bps * d->nch;
- return d->len * mul + t + 1;
+ int t = d->bps * d->nch;
+ return d->len * mul + t + 1;
}
// Calculate average ratio of filter output size to input size
-double af_calc_filter_multiplier(struct af_stream* s)
+double af_calc_filter_multiplier(struct af_stream *s)
{
- struct af_instance* af=s->first;
- double mul = 1;
- // Iterate through all filters and calculate total multiplication factor
- do{
- mul *= af->mul;
- af=af->next;
- }while(af);
+ struct af_instance *af = s->first;
+ double mul = 1;
+ // Iterate through all filters and calculate total multiplication factor
+ do {
+ mul *= af->mul;
+ af = af->next;
+ } while (af);
- return mul;
+ return mul;
}
/* Calculate the total delay [bytes output] caused by the filters */
-double af_calc_delay(struct af_stream* s)
+double af_calc_delay(struct af_stream *s)
{
- struct af_instance* af=s->first;
- register double delay = 0.0;
- // Iterate through all filters
- while(af){
- delay += af->delay;
- delay *= af->mul;
- af=af->next;
- }
- return delay;
+ struct af_instance *af = s->first;
+ register double delay = 0.0;
+ // Iterate through all filters
+ while (af) {
+ delay += af->delay;
+ delay *= af->mul;
+ af = af->next;
+ }
+ return delay;
}
/* Helper function called by the macro with the same name this
function should not be called directly */
-int af_resize_local_buffer(struct af_instance* af, struct mp_audio* data)
-{
- // Calculate new length
- register int len = af_lencalc(af->mul,data);
- mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
- "old len = %i, new len = %i\n",af->info->name,af->data->len,len);
- // If there is a buffer free it
- free(af->data->audio);
- // Create new buffer and check that it is OK
- af->data->audio = malloc(len);
- if(!af->data->audio){
- mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
- return AF_ERROR;
- }
- af->data->len=len;
- return AF_OK;
+int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data)
+{
+ // Calculate new length
+ register int len = af_lencalc(af->mul, data);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n", af->info->name, af->data->len, len);
+ // If there is a buffer free it
+ free(af->data->audio);
+ // Create new buffer and check that it is OK
+ af->data->audio = malloc(len);
+ if (!af->data->audio) {
+ mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
+ return AF_ERROR;
+ }
+ af->data->len = len;
+ return AF_OK;
}
// documentation in af.h
-struct af_instance *af_control_any_rev (struct af_stream* s, int cmd, void* arg) {
- int res = AF_UNKNOWN;
- struct af_instance* filt = s->last;
- while (filt) {
- res = filt->control(filt, cmd, arg);
- if (res == AF_OK)
- return filt;
- filt = filt->prev;
- }
- return NULL;
-}
-
-void af_help (void) {
- int i = 0;
- mp_msg(MSGT_AFILTER, MSGL_INFO, "Available audio filters:\n");
- while (filter_list[i]) {
- if (filter_list[i]->comment && filter_list[i]->comment[0])
- mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s (%s)\n", filter_list[i]->name, filter_list[i]->info, filter_list[i]->comment);
- else
- mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s\n", filter_list[i]->name, filter_list[i]->info);
- i++;
- }
+struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg)
+{
+ int res = AF_UNKNOWN;
+ struct af_instance *filt = s->last;
+ while (filt) {
+ res = filt->control(filt, cmd, arg);
+ if (res == AF_OK)
+ return filt;
+ filt = filt->prev;
+ }
+ return NULL;
}
-void af_fix_parameters(struct mp_audio *data)
+void af_help(void)
{
- if (data->nch < 0 || data->nch > AF_NCH) {
- mp_msg(MSGT_AFILTER, MSGL_ERR, "Invalid number of channels %i, assuming 2.\n", data->nch);
- data->nch = 2;
+ int i = 0;
+ mp_msg(MSGT_AFILTER, MSGL_INFO, "Available audio filters:\n");
+ while (filter_list[i]) {
+ if (filter_list[i]->comment && filter_list[i]->comment[0]) {
+ mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s (%s)\n",
+ filter_list[i]->name, filter_list[i]->info,
+ filter_list[i]->comment);
+ } else {
+ mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s\n",
+ filter_list[i]->name,
+ filter_list[i]->info);
+ }
+ i++;
}
- data->bps = af_fmt2bits(data->format)/8;
}
diff --git a/audio/filter/af.h b/audio/filter/af.h
index 31abe1edee..4ccc70792f 100644
--- a/audio/filter/af.h
+++ b/audio/filter/af.h
@@ -20,31 +20,21 @@
#define MPLAYER_AF_H
#include <stdio.h>
+#include <stdbool.h>
#include "config.h"
#include "core/options.h"
#include "audio/format.h"
+#include "audio/chmap.h"
+#include "audio/audio.h"
#include "control.h"
#include "core/mp_msg.h"
struct af_instance;
// Number of channels
-#ifndef AF_NCH
-#define AF_NCH 8
-#endif
-
-// Audio data chunk
-struct mp_audio {
- void *audio; // data buffer
- int len; // buffer length
- int rate; // sample rate
- int nch; // number of channels
- int format; // format
- int bps; // bytes per sample
-};
-
+#define AF_NCH MP_NUM_CHANNELS
// Flags used for defining the behavior of an audio filter
#define AF_FLAGS_REENTRANT 0x00000000
@@ -59,6 +49,7 @@ struct af_info {
const char *comment;
const int flags;
int (*open)(struct af_instance *vf);
+ bool (*test_conversion)(int src_format, int dst_format);
};
// Linked list of audio filters
@@ -75,29 +66,11 @@ struct af_instance {
* corresponding output */
double mul; /* length multiplier: how much does this instance change
the length of the buffer. */
+ bool auto_inserted; // inserted by af.c, such as conversion filters
};
-// Initialization flags
-extern int *af_cpu_speed;
-
-#define AF_INIT_AUTO 0x00000000
-#define AF_INIT_SLOW 0x00000001
-#define AF_INIT_FAST 0x00000002
-#define AF_INIT_FORCE 0x00000003
-#define AF_INIT_TYPE_MASK 0x00000003
-
-#define AF_INIT_INT 0x00000000
-#define AF_INIT_FLOAT 0x00000004
-#define AF_INIT_FORMAT_MASK 0x00000004
-
-// Default init type
-#ifndef AF_INIT_TYPE
-#define AF_INIT_TYPE (af_cpu_speed ? *af_cpu_speed : AF_INIT_SLOW)
-#endif
-
// Configuration switches
struct af_cfg {
- int force; // Initialization type
char **list; /* list of names of filters that are added to filter
list during first initialization of stream */
};
@@ -107,9 +80,12 @@ struct af_stream {
// The first and last filter in the list
struct af_instance *first;
struct af_instance *last;
- // Storage for input and output data formats
+ // The user sets the input format (what the decoder outputs), and sets some
+ // or all fields in output to the output format the AO accepts.
+ // See fixup_output_format().
struct mp_audio input;
struct mp_audio output;
+ struct mp_audio filter_output;
// Configuration for this stream
struct af_cfg cfg;
struct MPOpts *opts;
@@ -139,6 +115,9 @@ struct af_stream {
* \param s filter chain
*/
+struct af_stream *af_new(struct MPOpts *opts);
+void af_destroy(struct af_stream *s);
+
/**
* \brief Initialize the stream "s".
* \return 0 on success, -1 on failure
@@ -161,10 +140,10 @@ void af_uninit(struct af_stream *s);
/**
* \brief Reinit the filter list from the given filter on downwards
- * \param Filter instance to begin the reinit from
+ * See af.c.
* \return AF_OK on success or AF_ERROR on failure
*/
-int af_reinit(struct af_stream *s, struct af_instance *af);
+int af_reinit(struct af_stream *s);
/**
* \brief This function adds the filter "name" to the stream s.
@@ -306,23 +285,13 @@ float af_softclip(float a);
/** Print a list of all available audio filters */
void af_help(void);
-/**
- * \brief fill the missing parameters in the struct mp_audio structure
- * \param data structure to fill
- * \ingroup af_filter
- *
- * Currently only sets bps based on format
- */
-void af_fix_parameters(struct mp_audio *data);
-
/** Memory reallocation macro: if a local buffer is used (i.e. if the
filter doesn't operate on the incoming buffer this macro must be
called to ensure the buffer is big enough.
* \ingroup af_filter
*/
#define RESIZE_LOCAL_BUFFER(a, d) \
- ((a->data->len < \
- af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK)
+ ((a->data->len < af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK)
/* Some other useful macro definitions*/
#ifndef min
diff --git a/audio/filter/af_bs2b.c b/audio/filter/af_bs2b.c
index aebcc3b201..f8003a70a3 100644
--- a/audio/filter/af_bs2b.c
+++ b/audio/filter/af_bs2b.c
@@ -105,9 +105,8 @@ static int control(struct af_instance *af, int cmd, void *arg)
format = ((struct mp_audio*)arg)->format;
af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = 2; // bs2b is useful only for 2ch audio
- af->data->bps = ((struct mp_audio*)arg)->bps;
- af->data->format = format;
+ mp_audio_set_num_channels(af->data, 2); // bs2b is useful only for 2ch audio
+ mp_audio_set_format(af->data, format);
/* check for formats supported by libbs2b
and assign corresponding handlers */
@@ -162,8 +161,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
break;
default:
af->play = play_f;
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
break;
}
diff --git a/audio/filter/af_center.c b/audio/filter/af_center.c
index aa9aae8514..42571eea35 100644
--- a/audio/filter/af_center.c
+++ b/audio/filter/af_center.c
@@ -48,9 +48,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch);
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_set_channels_old(af->data, max(s->ch+1,((struct mp_audio*)arg)->nch));
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
return af_test_output(af,(struct mp_audio*)arg);
}
diff --git a/audio/filter/af_channels.c b/audio/filter/af_channels.c
index 8f676d8cfd..7955018ec0 100644
--- a/audio/filter/af_channels.c
+++ b/audio/filter/af_channels.c
@@ -164,8 +164,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
}
af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->format = ((struct mp_audio*)arg)->format;
- af->data->bps = ((struct mp_audio*)arg)->bps;
+ mp_audio_set_format(af->data, ((struct mp_audio*)arg)->format);
af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch);
case AF_CONTROL_COMMAND_LINE:{
@@ -194,54 +193,20 @@ static int control(struct af_instance* af, int cmd, void* arg)
}
}
- if(AF_OK != af->control(af,AF_CONTROL_CHANNELS | AF_CONTROL_SET ,&nch))
+ struct mp_chmap chmap;
+ mp_chmap_from_channels(&chmap, nch);
+ if (AF_OK != af->control(af, AF_CONTROL_CHANNELS | AF_CONTROL_SET, &chmap))
return AF_ERROR;
return AF_OK;
}
case AF_CONTROL_CHANNELS | AF_CONTROL_SET:
// Reinit must be called after this function has been called
- // Sanity check
- if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
- mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of output channels must be"
- " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
- return AF_ERROR;
- }
-
- af->data->nch=((int*)arg)[0];
+ mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
if(!s->router)
mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels"
" to %i\n",af->data->nch);
return AF_OK;
- case AF_CONTROL_CHANNELS | AF_CONTROL_GET:
- *(int*)arg = af->data->nch;
- return AF_OK;
- case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_SET:{
- int ch = ((af_control_ext_t*)arg)->ch;
- int* route = ((af_control_ext_t*)arg)->arg;
- s->route[ch][FR] = route[FR];
- s->route[ch][TO] = route[TO];
- return AF_OK;
- }
- case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_GET:{
- int ch = ((af_control_ext_t*)arg)->ch;
- int* route = ((af_control_ext_t*)arg)->arg;
- route[FR] = s->route[ch][FR];
- route[TO] = s->route[ch][TO];
- return AF_OK;
- }
- case AF_CONTROL_CHANNELS_NR | AF_CONTROL_SET:
- s->nr = *(int*)arg;
- return AF_OK;
- case AF_CONTROL_CHANNELS_NR | AF_CONTROL_GET:
- *(int*)arg = s->nr;
- return AF_OK;
- case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_SET:
- s->router = *(int*)arg;
- return AF_OK;
- case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_GET:
- *(int*)arg = s->router;
- return AF_OK;
}
return AF_UNKNOWN;
}
@@ -277,7 +242,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
// Set output data
c->audio = l->audio;
c->len = c->len / c->nch * l->nch;
- c->nch = l->nch;
+ mp_audio_set_channels(c, &l->channels);
return c;
}
diff --git a/audio/filter/af_delay.c b/audio/filter/af_delay.c
index ce8d71980b..eb3e9e19cb 100644
--- a/audio/filter/af_delay.c
+++ b/audio/filter/af_delay.c
@@ -52,10 +52,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
for(i=0;i<af->data->nch;i++)
free(s->q[i]);
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
- af->data->format = ((struct mp_audio*)arg)->format;
- af->data->bps = ((struct mp_audio*)arg)->bps;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
// Allocate new delay queues
for(i=0;i<af->data->nch;i++){
diff --git a/audio/filter/af_drc.c b/audio/filter/af_drc.c
index ba1e10c0a7..e293f7f616 100644
--- a/audio/filter/af_drc.c
+++ b/audio/filter/af_drc.c
@@ -87,15 +87,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Sanity check
if(!arg) return AF_ERROR;
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
-
- if(((struct mp_audio*)arg)->format == (AF_FORMAT_S16_NE)){
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
- }else{
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+
+ if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16_NE)){
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
}
return af_test_output(af,(struct mp_audio*)arg);
case AF_CONTROL_COMMAND_LINE:{
diff --git a/audio/filter/af_dummy.c b/audio/filter/af_dummy.c
index 29a5b3d4b8..5a54cdd80c 100644
--- a/audio/filter/af_dummy.c
+++ b/audio/filter/af_dummy.c
@@ -29,8 +29,8 @@
static int control(struct af_instance* af, int cmd, void* arg)
{
switch(cmd){
- case AF_CONTROL_REINIT:
- memcpy(af->data,(struct mp_audio*)arg,sizeof(struct mp_audio));
+ case AF_CONTROL_REINIT: ;
+ *af->data = *(struct mp_audio*)arg;
mp_msg(MSGT_AFILTER, MSGL_V, "[dummy] Was reinitialized: %iHz/%ich/%s\n",
af->data->rate,af->data->nch,af_fmt2str_short(af->data->format));
return AF_OK;
diff --git a/audio/filter/af_equalizer.c b/audio/filter/af_equalizer.c
index c488ffaeaf..6441b9b116 100644
--- a/audio/filter/af_equalizer.c
+++ b/audio/filter/af_equalizer.c
@@ -96,10 +96,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Sanity check
if(!arg) return AF_ERROR;
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
// Calculate number of active filters
s->K=KM;
@@ -150,30 +148,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
}
return AF_OK;
}
- case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET:{
- float* gain = ((af_control_ext_t*)arg)->arg;
- int ch = ((af_control_ext_t*)arg)->ch;
- int k;
- if(ch >= AF_NCH || ch < 0)
- return AF_ERROR;
-
- for(k = 0 ; k<KM ; k++)
- s->g[ch][k] = pow(10.0,clamp(gain[k],G_MIN,G_MAX)/20.0)-1.0;
-
- return AF_OK;
- }
- case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_GET:{
- float* gain = ((af_control_ext_t*)arg)->arg;
- int ch = ((af_control_ext_t*)arg)->ch;
- int k;
- if(ch >= AF_NCH || ch < 0)
- return AF_ERROR;
-
- for(k = 0 ; k<KM ; k++)
- gain[k] = log10(s->g[ch][k]+1.0) * 20.0;
-
- return AF_OK;
- }
}
return AF_UNKNOWN;
}
diff --git a/audio/filter/af_export.c b/audio/filter/af_export.c
index 5e3a1869ee..ea0aa938ca 100644
--- a/audio/filter/af_export.c
+++ b/audio/filter/af_export.c
@@ -85,10 +85,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
close(s->fd);
// Accept only int16_t as input format (which sucks)
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
// If buffer length isn't set, set it to the default value
if(s->sz == 0)
diff --git a/audio/filter/af_extrastereo.c b/audio/filter/af_extrastereo.c
index 0f7fe36861..ed1fd27898 100644
--- a/audio/filter/af_extrastereo.c
+++ b/audio/filter/af_extrastereo.c
@@ -47,17 +47,14 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Sanity check
if(!arg) return AF_ERROR;
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = 2;
- if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_num_channels(af->data, 2);
+ if (af->data->format == AF_FORMAT_FLOAT_NE)
{
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
af->play = play_float;
}// else
{
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
+ mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
af->play = play_s16;
}
@@ -69,12 +66,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->mul = f;
return AF_OK;
}
- case AF_CONTROL_ES_MUL | AF_CONTROL_SET:
- s->mul = *(float*)arg;
- return AF_OK;
- case AF_CONTROL_ES_MUL | AF_CONTROL_GET:
- *(float*)arg = s->mul;
- return AF_OK;
}
return AF_UNKNOWN;
}
diff --git a/audio/filter/af_force.c b/audio/filter/af_force.c
new file mode 100644
index 0000000000..51fe83d0f0
--- /dev/null
+++ b/audio/filter/af_force.c
@@ -0,0 +1,146 @@
+/*
+ * 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 <libavutil/common.h>
+
+#include "core/m_config.h"
+#include "core/m_option.h"
+
+#include "audio/format.h"
+#include "af.h"
+
+struct priv {
+ struct m_config *config;
+
+ int in_format;
+ int in_srate;
+ struct mp_chmap in_channels;
+ int out_format;
+ int out_srate;
+ struct mp_chmap out_channels;
+
+ struct mp_audio data;
+ struct mp_audio temp;
+};
+
+static const struct priv defaults = {
+ .in_format = AF_FORMAT_UNKNOWN,
+ .out_format = AF_FORMAT_UNKNOWN,
+};
+
+#define OPT_BASE_STRUCT struct priv
+
+static const struct m_option options[] = {
+ OPT_AUDIOFORMAT("format", in_format, 0),
+ OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000),
+ OPT_CHMAP("channels", in_channels, CONF_MIN, .min = 0),
+ OPT_AUDIOFORMAT("out-format", out_format, 0),
+ OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000),
+ OPT_CHMAP("out-channels", out_channels, CONF_MIN, .min = 0),
+ {0}
+};
+
+static int control(struct af_instance *af, int cmd, void *arg)
+{
+ struct priv *priv = af->setup;
+
+ switch (cmd) {
+ case AF_CONTROL_REINIT: {
+ struct mp_audio *in = arg;
+ struct mp_audio orig_in = *in;
+ struct mp_audio *out = af->data;
+
+ if (priv->in_format != AF_FORMAT_UNKNOWN)
+ mp_audio_set_format(in, priv->in_format);
+
+ if (priv->in_channels.num)
+ mp_audio_set_channels(in, &priv->in_channels);
+
+ if (priv->in_srate)
+ in->rate = priv->in_srate;
+
+ mp_audio_copy_config(out, in);
+
+ if (priv->out_format != AF_FORMAT_UNKNOWN)
+ mp_audio_set_format(out, priv->out_format);
+
+ if (priv->out_channels.num)
+ mp_audio_set_channels(out, &priv->out_channels);
+
+ if (priv->out_srate)
+ out->rate = priv->out_srate;
+
+ if (in->nch != out->nch || in->bps != out->bps) {
+ mp_msg(MSGT_AFILTER, MSGL_ERR,
+ "[af_force] Forced input/output format are incompatible.\n");
+ return AF_ERROR;
+ }
+
+ return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
+ }
+ case AF_CONTROL_COMMAND_LINE: {
+ if (m_config_parse_suboptions(priv->config, "af_force", (char *)arg) < 0)
+ return AF_ERROR;
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
+{
+ struct priv *priv = af->setup;
+ struct mp_audio *r = &priv->temp;
+
+ *r = *af->data;
+ r->audio = data->audio;
+ r->len = data->len;
+
+ return r;
+}
+
+static void uninit(struct af_instance *af)
+{
+ talloc_free(af->setup);
+}
+
+static int af_open(struct af_instance *af)
+{
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul = 1;
+ struct priv *priv = talloc(NULL, struct priv);
+ af->setup = priv;
+ *priv = defaults;
+ priv->config = m_config_simple(priv);
+ talloc_steal(priv, priv->config);
+ m_config_register_options(priv->config, options);
+ af->data = &priv->data;
+ return AF_OK;
+}
+
+struct af_info af_info_force = {
+ "Force audio format",
+ "force",
+ "",
+ "",
+ 0,
+ af_open
+};
diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c
index 37d29c1f80..616bc9e494 100644
--- a/audio/filter/af_format.c
+++ b/audio/filter/af_format.c
@@ -75,6 +75,14 @@ static int check_format(int format)
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)
{
@@ -86,8 +94,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
int supported_ac3 = 0;
// Make sure this filter isn't redundant
- if(af->data->format == data->format &&
- af->data->bps == data->bps)
+ if(af->data->format == data->format)
return AF_DETACH;
// A bit complex because we can convert AC3
@@ -113,7 +120,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
buf1, buf2);
af->data->rate = data->rate;
- af->data->nch = data->nch;
+ mp_audio_set_channels(af->data, &data->channels);
af->mul = (double)af->data->bps / data->bps;
af->play = play; // set default
@@ -147,7 +154,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
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))
+ if(AF_OK != af->control(af, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format))
return AF_ERROR;
return AF_OK;
}
@@ -156,8 +163,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg))
return AF_ERROR;
- af->data->format = *(int*)arg;
- af->data->bps = af_fmt2bits(af->data->format)/8;
+ mp_audio_set_format(af->data, *(int*)arg);
return AF_OK;
}
@@ -186,7 +192,7 @@ static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio*
endian(c->audio,l->audio,len,c->bps);
c->audio = l->audio;
- c->format = l->format;
+ mp_audio_set_format(c, l->format);
return c;
}
@@ -203,9 +209,8 @@ static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio*
float2int(c->audio, l->audio, len, 2);
c->audio = l->audio;
+ mp_audio_set_format(c, l->format);
c->len = len*2;
- c->bps = 2;
- c->format = l->format;
return c;
}
@@ -222,9 +227,8 @@ static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio*
int2float(c->audio, l->audio, len, 2);
c->audio = l->audio;
+ mp_audio_set_format(c, l->format);
c->len = len*4;
- c->bps = 4;
- c->format = l->format;
return c;
}
@@ -276,9 +280,8 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
// Set output data
c->audio = l->audio;
+ mp_audio_set_format(c, l->format);
c->len = len*l->bps;
- c->bps = l->bps;
- c->format = l->format;
return c;
}
@@ -301,7 +304,8 @@ struct af_info af_info_format = {
"Anders",
"",
AF_FLAGS_REENTRANT,
- af_open
+ af_open,
+ .test_conversion = test_conversion,
};
static inline uint32_t load24bit(void* data, int pos) {
diff --git a/audio/filter/af_hrtf.c b/audio/filter/af_hrtf.c
index 4f5eedb29d..85e6477b31 100644
--- a/audio/filter/af_hrtf.c
+++ b/audio/filter/af_hrtf.c
@@ -299,7 +299,7 @@ static int control(struct af_instance *af, int cmd, void* arg)
af->data->rate);
return AF_ERROR;
}
- af->data->nch = ((struct mp_audio*)arg)->nch;
+ mp_audio_set_channels_old(af->data, ((struct mp_audio*)arg)->nch);
if(af->data->nch == 2) {
/* 2 channel input */
if(s->decode_mode != HRTF_MIX_MATRIX2CH) {
@@ -308,13 +308,12 @@ static int control(struct af_instance *af, int cmd, void* arg)
}
}
else if (af->data->nch < 5)
- af->data->nch = 5;
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
+ mp_audio_set_channels_old(af->data, 5);
+ mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
test_output_res = af_test_output(af, (struct mp_audio*)arg);
af->mul = 2.0 / af->data->nch;
// after testing input set the real output format
- af->data->nch = 2;
+ mp_audio_set_num_channels(af->data, 2);
s->print_flag = 1;
return test_output_res;
case AF_CONTROL_COMMAND_LINE:
@@ -566,7 +565,7 @@ static struct mp_audio* play(struct af_instance *af, struct mp_audio *data)
/* Set output data */
data->audio = af->data->audio;
data->len = data->len / data->nch * 2;
- data->nch = 2;
+ mp_audio_set_num_channels(data, 2);
return data;
}
diff --git a/audio/filter/af_karaoke.c b/audio/filter/af_karaoke.c
index 965eb8f40d..faed389625 100644
--- a/audio/filter/af_karaoke.c
+++ b/audio/filter/af_karaoke.c
@@ -34,10 +34,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
{
switch(cmd){
case AF_CONTROL_REINIT:
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
- af->data->format= AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
return af_test_output(af,(struct mp_audio*)arg);
}
return AF_UNKNOWN;
diff --git a/audio/filter/af_ladspa.c b/audio/filter/af_ladspa.c
index c1b3f24360..5e516f060e 100644
--- a/audio/filter/af_ladspa.c
+++ b/audio/filter/af_ladspa.c
@@ -291,7 +291,7 @@ static int af_ladspa_parse_plugin(af_ladspa_t *setup) {
static void* mydlopen(const char *filename, int flag) {
char *buf;
const char *end, *start, *ladspapath;
- int endsinso, needslash;
+ int endsinso;
size_t filenamelen;
void *result = NULL;
@@ -324,9 +324,9 @@ static void* mydlopen(const char *filename, int flag) {
ladspapath=getenv("LADSPA_PATH");
if (ladspapath) {
-
start=ladspapath;
while (*start != '\0') {
+ int needslash;
end=start;
while ( (*end != ':') && (*end != '\0') )
end++;
@@ -487,7 +487,6 @@ static int af_ladspa_malloc_failed(char *myname) {
static int control(struct af_instance *af, int cmd, void *arg) {
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
- int i, r;
float val;
switch(cmd) {
@@ -498,10 +497,8 @@ static int control(struct af_instance *af, int cmd, void *arg) {
/* accept FLOAT, let af_format do conversion */
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
/* arg->len is not set here yet, so init of buffers and connecting the
* filter, has to be done in play() :-/
@@ -538,7 +535,10 @@ static int control(struct af_instance *af, int cmd, void *arg) {
}
line += strlen(buf);
setup->file = strdup(buf);
- if (!setup->file) return af_ladspa_malloc_failed(setup->myname);
+ if (!setup->file) {
+ free(buf);
+ return af_ladspa_malloc_failed(setup->myname);
+ }
mp_msg(MSGT_AFILTER, MSGL_V, "%s: file --> %s\n", setup->myname,
setup->file);
if (*line != '\0') line++; /* read ':' */
@@ -554,7 +554,10 @@ static int control(struct af_instance *af, int cmd, void *arg) {
}
line += strlen(buf);
setup->label = strdup(buf);
- if (!setup->label) return af_ladspa_malloc_failed(setup->myname);
+ if (!setup->label) {
+ free(buf);
+ return af_ladspa_malloc_failed(setup->myname);
+ }
mp_msg(MSGT_AFILTER, MSGL_V, "%s: label --> %s\n", setup->myname,
setup->label);
/* if (*line != '0') line++; */ /* read ':' */
@@ -581,15 +584,14 @@ static int control(struct af_instance *af, int cmd, void *arg) {
/* ninputcontrols is set by now, read control values from arg */
- for(i=0; i<setup->ninputcontrols; i++) {
+ for (int i = 0; i < setup->ninputcontrols; i++) {
if (!line || *line != ':') {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("Not enough controls specified on the command line."));
return AF_ERROR;
}
line++;
- r = sscanf(line, "%f", &val);
- if (r!=1) {
+ if (sscanf(line, "%f", &val) != 1) {
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
_("Not enough controls specified on the command line."));
return AF_ERROR;
@@ -599,7 +601,7 @@ static int control(struct af_instance *af, int cmd, void *arg) {
}
mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname);
- for(i=0; i<setup->ninputcontrols; i++) {
+ for (int i = 0; i < setup->ninputcontrols; i++) {
mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ",
setup->inputcontrols[setup->inputcontrolsmap[i]]);
}
@@ -609,7 +611,7 @@ static int control(struct af_instance *af, int cmd, void *arg) {
mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n",
setup->myname);
- for(i=0; i<setup->ninputcontrols; i++) {
+ for (int i = 0; i < setup->ninputcontrols; i++) {
int p = setup->inputcontrolsmap[i];
LADSPA_PortRangeHint hint =
setup->plugin_descriptor->PortRangeHints[p];
@@ -651,8 +653,6 @@ static int control(struct af_instance *af, int cmd, void *arg) {
*/
static void uninit(struct af_instance *af) {
- int i;
-
free(af->data);
if (af->setup) {
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
@@ -664,7 +664,7 @@ static void uninit(struct af_instance *af) {
}
if (setup->chhandles) {
- for(i=0; i<setup->nch; i+=setup->ninputs) {
+ for (int i = 0; i < setup->nch; i+=setup->ninputs) {
if (pdes->deactivate) pdes->deactivate(setup->chhandles[i]);
if (pdes->cleanup) pdes->cleanup(setup->chhandles[i]);
}
@@ -681,13 +681,13 @@ static void uninit(struct af_instance *af) {
free(setup->outputs);
if (setup->inbufs) {
- for(i=0; i<setup->nch; i++)
+ for(int i = 0; i < setup->nch; i++)
free(setup->inbufs[i]);
free(setup->inbufs);
}
if (setup->outbufs) {
- for(i=0; i<setup->nch; i++)
+ for (int i = 0; i < setup->nch; i++)
free(setup->outbufs[i]);
free(setup->outbufs);
}
diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c
index 94d59a11ca..7eacc01d81 100644
--- a/audio/filter/af_lavcac3enc.c
+++ b/audio/filter/af_lavcac3enc.c
@@ -75,16 +75,16 @@ static int control(struct af_instance *af, int cmd, void *arg)
if (AF_FORMAT_IS_AC3(data->format) || data->nch < s->min_channel_num)
return AF_DETACH;
- af->data->format = s->in_sampleformat;
- af->data->bps = af_fmt2bits(s->in_sampleformat) / 8;
+ mp_audio_set_format(af->data, s->in_sampleformat);
if (data->rate == 48000 || data->rate == 44100 || data->rate == 32000)
af->data->rate = data->rate;
else
af->data->rate = 48000;
if (data->nch > AC3_MAX_CHANNELS)
- af->data->nch = AC3_MAX_CHANNELS;
+ mp_audio_set_num_channels(af->data, AC3_MAX_CHANNELS);
else
- af->data->nch = data->nch;
+ mp_audio_set_channels(af->data, &data->channels);
+ mp_chmap_reorder_to_lavc(&af->data->channels);
test_output_res = af_test_output(af, data);
s->pending_len = 0;
@@ -108,8 +108,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
// Put sample parameters
s->lavc_actx->channels = af->data->nch;
- s->lavc_actx->channel_layout =
- av_get_default_channel_layout(af->data->nch);
+ s->lavc_actx->channel_layout = mp_chmap_to_lavc(&af->data->channels);
s->lavc_actx->sample_rate = af->data->rate;
s->lavc_actx->bit_rate = bit_rate;
@@ -123,9 +122,8 @@ static int control(struct af_instance *af, int cmd, void *arg)
"encoder frame size %d\n", s->lavc_actx->frame_size);
return AF_ERROR;
}
- af->data->format = AF_FORMAT_AC3_BE;
- af->data->bps = 2;
- af->data->nch = 2;
+ mp_audio_set_format(af->data, AF_FORMAT_AC3_BE);
+ mp_audio_set_num_channels(af->data, 2);
return test_output_res;
case AF_CONTROL_COMMAND_LINE:
mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc cmdline: %s.\n", (char*)arg);
@@ -235,15 +233,6 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
src2= s->pending_data;
}
- if (c->nch >= 5) {
- reorder_channel_nch(src2,
- AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
- AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
- c->nch,
- s->expect_len / samplesize,
- samplesize);
- }
-
void *data = (void *) src2;
if (s->planarize) {
void *data2 = malloc(s->expect_len);
@@ -316,8 +305,8 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
buf += len;
}
c->audio = l->audio;
- c->nch = 2;
- c->bps = 2;
+ mp_audio_set_num_channels(c, 2);
+ mp_audio_set_format(c, af->data->format);
c->len = outsize;
mp_msg(MSGT_AFILTER, MSGL_DBG2, "play return size %d, pending %d\n",
outsize, s->pending_len);
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c
index 5b26a0dce6..4372100ea0 100644
--- a/audio/filter/af_lavrresample.c
+++ b/audio/filter/af_lavrresample.c
@@ -43,6 +43,7 @@
#define avresample_available(x) 0
#define avresample_convert(ctx, out, out_planesize, out_samples, in, in_planesize, in_samples) \
swr_convert(ctx, out, out_samples, (const uint8_t**)(in), in_samples)
+#define avresample_set_channel_mapping swr_set_channel_mapping
#else
#error "config.h broken"
#endif
@@ -50,6 +51,7 @@
#include "core/mp_msg.h"
#include "core/subopt-helper.h"
#include "audio/filter/af.h"
+#include "audio/fmt-conversion.h"
struct af_resample_opts {
int filter_size;
@@ -57,14 +59,24 @@ struct af_resample_opts {
int linear;
double cutoff;
- int out_rate;
int in_rate;
+ int in_format;
+ struct mp_chmap in_channels;
+ int out_rate;
+ int out_format;
+ struct mp_chmap out_channels;
};
struct af_resample {
+ int allow_detach;
struct AVAudioResampleContext *avrctx;
+ struct AVAudioResampleContext *avrctx_out; // for output channel reordering
struct af_resample_opts ctx; // opts in the context
struct af_resample_opts opts; // opts requested by the user
+ // At least libswresample keeps a pointer around for this:
+ int reorder_in[MP_NUM_CHANNELS];
+ int reorder_out[MP_NUM_CHANNELS];
+ uint8_t *reorder_buffer;
};
#ifdef CONFIG_LIBAVRESAMPLE
@@ -88,8 +100,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s,
struct mp_audio *in,
struct mp_audio *out)
{
- return s->ctx.out_rate != out->rate ||
- s->ctx.in_rate != in->rate ||
+ return s->ctx.in_rate != in->rate ||
+ s->ctx.in_format != in->format ||
+ !mp_chmap_equals(&s->ctx.in_channels, &in->channels) ||
+ s->ctx.out_rate != out->rate ||
+ s->ctx.out_format != out->format ||
+ !mp_chmap_equals(&s->ctx.out_channels, &out->channels) ||
s->ctx.filter_size != s->opts.filter_size ||
s->ctx.phase_shift != s->opts.phase_shift ||
s->ctx.linear != s->opts.linear ||
@@ -97,6 +113,12 @@ static bool needs_lavrctx_reconfigure(struct af_resample *s,
}
+static bool test_conversion(int src_format, int dst_format)
+{
+ return af_to_avformat(src_format) != AV_SAMPLE_FMT_NONE &&
+ af_to_avformat(dst_format) != AV_SAMPLE_FMT_NONE;
+}
+
#define ctx_opt_set_int(a,b) av_opt_set_int(s->avrctx, (a), (b), 0)
#define ctx_opt_set_dbl(a,b) av_opt_set_double(s->avrctx, (a), (b), 0)
@@ -108,36 +130,70 @@ static int control(struct af_instance *af, int cmd, void *arg)
switch (cmd) {
case AF_CONTROL_REINIT: {
- if ((out->rate == in->rate) || (out->rate == 0))
+ struct mp_audio orig_in = *in;
+
+ if (((out->rate == in->rate) || (out->rate == 0)) &&
+ (out->format == in->format) &&
+ (mp_chmap_equals(&out->channels, &in->channels) || out->nch == 0) &&
+ s->allow_detach)
return AF_DETACH;
- out->nch = FFMIN(in->nch, AF_NCH);
- out->format = AF_FORMAT_S16_NE;
- out->bps = 2;
- af->mul = (double) out->rate / in->rate;
+ if (out->rate == 0)
+ out->rate = in->rate;
+
+ if (mp_chmap_is_empty(&out->channels))
+ mp_audio_set_channels(out, &in->channels);
+
+ enum AVSampleFormat in_samplefmt = af_to_avformat(in->format);
+ if (in_samplefmt == AV_SAMPLE_FMT_NONE) {
+ mp_audio_set_format(in, AF_FORMAT_FLOAT_NE);
+ in_samplefmt = af_to_avformat(in->format);
+ }
+ enum AVSampleFormat out_samplefmt = af_to_avformat(out->format);
+ if (out_samplefmt == AV_SAMPLE_FMT_NONE) {
+ mp_audio_set_format(out, in->format);
+ out_samplefmt = in_samplefmt;
+ }
+
+ af->mul = (double) (out->rate * out->nch) / (in->rate * in->nch);
af->delay = out->nch * s->opts.filter_size / FFMIN(af->mul, 1);
if (needs_lavrctx_reconfigure(s, in, out)) {
- if (s->avrctx)
- avresample_close(s->avrctx);
+ avresample_close(s->avrctx);
+ avresample_close(s->avrctx_out);
s->ctx.out_rate = out->rate;
s->ctx.in_rate = in->rate;
+ s->ctx.out_format = out->format;
+ s->ctx.in_format = in->format;
+ s->ctx.out_channels= out->channels;
+ s->ctx.in_channels = in->channels;
s->ctx.filter_size = s->opts.filter_size;
s->ctx.phase_shift = s->opts.phase_shift;
s->ctx.linear = s->opts.linear;
s->ctx.cutoff = s->opts.cutoff;
- int ch_layout = av_get_default_channel_layout(out->nch);
+ struct mp_chmap map_in = in->channels;
+ struct mp_chmap map_out = out->channels;
+
+ // Try not to do any remixing if at least one is "unknown".
+ if (mp_chmap_is_unknown(&map_in) || mp_chmap_is_unknown(&map_out)) {
+ mp_chmap_set_unknown(&map_in, map_in.num);
+ mp_chmap_set_unknown(&map_out, map_out.num);
+ }
+
+ // unchecked: don't take any channel reordering into account
+ uint64_t in_ch_layout = mp_chmap_to_lavc_unchecked(&map_in);
+ uint64_t out_ch_layout = mp_chmap_to_lavc_unchecked(&map_out);
- ctx_opt_set_int("in_channel_layout", ch_layout);
- ctx_opt_set_int("out_channel_layout", ch_layout);
+ ctx_opt_set_int("in_channel_layout", in_ch_layout);
+ ctx_opt_set_int("out_channel_layout", out_ch_layout);
ctx_opt_set_int("in_sample_rate", s->ctx.in_rate);
ctx_opt_set_int("out_sample_rate", s->ctx.out_rate);
- ctx_opt_set_int("in_sample_fmt", AV_SAMPLE_FMT_S16);
- ctx_opt_set_int("out_sample_fmt", AV_SAMPLE_FMT_S16);
+ ctx_opt_set_int("in_sample_fmt", in_samplefmt);
+ ctx_opt_set_int("out_sample_fmt", out_samplefmt);
ctx_opt_set_int("filter_size", s->ctx.filter_size);
ctx_opt_set_int("phase_shift", s->ctx.phase_shift);
@@ -145,20 +201,51 @@ static int control(struct af_instance *af, int cmd, void *arg)
ctx_opt_set_dbl("cutoff", s->ctx.cutoff);
- if (avresample_open(s->avrctx) < 0) {
+ struct mp_chmap in_lavc;
+ mp_chmap_from_lavc(&in_lavc, in_ch_layout);
+ mp_chmap_get_reorder(s->reorder_in, &map_in, &in_lavc);
+
+ struct mp_chmap out_lavc;
+ mp_chmap_from_lavc(&out_lavc, out_ch_layout);
+ mp_chmap_get_reorder(s->reorder_out, &out_lavc, &map_out);
+
+ // Same configuration; we just reorder.
+ av_opt_set_int(s->avrctx_out, "in_channel_layout", out_ch_layout, 0);
+ av_opt_set_int(s->avrctx_out, "out_channel_layout", out_ch_layout, 0);
+ av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmt, 0);
+ av_opt_set_int(s->avrctx_out, "out_sample_fmt", out_samplefmt, 0);
+ av_opt_set_int(s->avrctx_out, "in_sample_rate", s->ctx.out_rate, 0);
+ av_opt_set_int(s->avrctx_out, "out_sample_rate", s->ctx.out_rate, 0);
+
+ // API has weird requirements, quoting avresample.h:
+ // * This function can only be called when the allocated context is not open.
+ // * Also, the input channel layout must have already been set.
+ avresample_set_channel_mapping(s->avrctx, s->reorder_in);
+ avresample_set_channel_mapping(s->avrctx_out, s->reorder_out);
+
+ if (avresample_open(s->avrctx) < 0 ||
+ avresample_open(s->avrctx_out) < 0)
+ {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot open "
"Libavresample Context. \n");
return AF_ERROR;
}
}
- int out_rate, test_output_res;
- // hack to make af_test_output ignore the samplerate change
- out_rate = out->rate;
- out->rate = in->rate;
- test_output_res = af_test_output(af, in);
- out->rate = out_rate;
- return test_output_res;
+ return ((in->format == orig_in.format) &&
+ mp_chmap_equals(&in->channels, &orig_in.channels))
+ ? AF_OK : AF_FALSE;
+ }
+ case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET: {
+ if (af_to_avformat(*(int*)arg) == AV_SAMPLE_FMT_NONE)
+ return AF_FALSE;
+
+ mp_audio_set_format(af->data, *(int*)arg);
+ return AF_OK;
+ }
+ case AF_CONTROL_CHANNELS | AF_CONTROL_SET: {
+ mp_audio_set_channels(af->data, (struct mp_chmap *)arg);
+ return AF_OK;
}
case AF_CONTROL_COMMAND_LINE: {
s->opts.cutoff = 0.0;
@@ -169,6 +256,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
{"phase_shift", OPT_ARG_INT, &s->opts.phase_shift, NULL},
{"linear", OPT_ARG_BOOL, &s->opts.linear, NULL},
{"cutoff", OPT_ARG_FLOAT, &s->opts.cutoff, NULL},
+ {"detach", OPT_ARG_BOOL, &s->allow_detach, NULL},
{0}
};
@@ -198,10 +286,21 @@ static void uninit(struct af_instance *af)
struct af_resample *s = af->setup;
if (s->avrctx)
avresample_close(s->avrctx);
+ if (s->avrctx_out)
+ avresample_close(s->avrctx_out);
talloc_free(af->setup);
}
}
+static bool needs_reorder(int *reorder, int num_ch)
+{
+ for (int n = 0; n < num_ch; n++) {
+ if (reorder[n] != n)
+ return true;
+ }
+ return false;
+}
+
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
{
struct af_resample *s = af->setup;
@@ -227,10 +326,18 @@ static struct mp_audio *play(struct af_instance *af, struct mp_audio *data)
(uint8_t **) &out->audio, out_size, out_samples,
(uint8_t **) &in->audio, in_size, in_samples);
- out_size = out->bps * out_samples * out->nch;
- in->audio = out->audio;
- in->len = out_size;
- in->rate = s->ctx.out_rate;
+ *data = *out;
+
+ if (needs_reorder(s->reorder_out, out->nch)) {
+ if (talloc_get_size(s->reorder_buffer) < out_size)
+ s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size);
+ data->audio = s->reorder_buffer;
+ out_samples = avresample_convert(s->avrctx_out,
+ (uint8_t **) &data->audio, out_size, out_samples,
+ (uint8_t **) &out->audio, out_size, out_samples);
+ }
+
+ data->len = out->bps * out_samples * out->nch;
return data;
}
@@ -244,7 +351,7 @@ static int af_open(struct af_instance *af)
af->mul = 1;
af->data = talloc_zero(s, struct mp_audio);
- af->data->rate = 44100;
+ af->data->rate = 0;
int default_filter_size = 16;
s->opts = (struct af_resample_opts) {
@@ -254,10 +361,13 @@ static int af_open(struct af_instance *af)
.phase_shift = 10,
};
+ s->allow_detach = 1;
+
s->avrctx = avresample_alloc_context();
+ s->avrctx_out = avresample_alloc_context();
af->setup = s;
- if (s->avrctx) {
+ if (s->avrctx && s->avrctx_out) {
return AF_OK;
} else {
mp_msg(MSGT_AFILTER, MSGL_ERR, "[lavrresample] Cannot initialize "
@@ -273,5 +383,6 @@ struct af_info af_info_lavrresample = {
"Stefano Pigozzi (based on Michael Niedermayer's lavcresample)",
"",
AF_FLAGS_REENTRANT,
- af_open
+ af_open,
+ .test_conversion = test_conversion,
};
diff --git a/audio/filter/af_pan.c b/audio/filter/af_pan.c
index 8b1783ee84..d6f7538868 100644
--- a/audio/filter/af_pan.c
+++ b/audio/filter/af_pan.c
@@ -34,6 +34,15 @@ typedef struct af_pan_s
float level[AF_NCH][AF_NCH]; // Gain level for each channel
}af_pan_t;
+static void set_channels(struct mp_audio *mpa, int num)
+{
+ struct mp_chmap map;
+ // "unknown" channel layouts make it easier to pass through audio data,
+ // without triggering remixing.
+ mp_chmap_set_unknown(&map, num);
+ mp_audio_set_channels(mpa, &map);
+}
+
// Initialization and runtime control
static int control(struct af_instance* af, int cmd, void* arg)
{
@@ -45,15 +54,13 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
- af->data->nch = s->nch ? s->nch: ((struct mp_audio*)arg)->nch;
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
+ set_channels(af->data, s->nch ? s->nch: ((struct mp_audio*)arg)->nch);
af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch;
if((af->data->format != ((struct mp_audio*)arg)->format) ||
(af->data->bps != ((struct mp_audio*)arg)->bps)){
- ((struct mp_audio*)arg)->format = af->data->format;
- ((struct mp_audio*)arg)->bps = af->data->bps;
+ mp_audio_set_format((struct mp_audio*)arg, af->data->format);
return AF_FALSE;
}
return AF_OK;
@@ -109,14 +116,11 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Sanity check
if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[pan] The number of output channels must be"
- " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
+ " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
return AF_ERROR;
}
s->nch=((int*)arg)[0];
return AF_OK;
- case AF_CONTROL_PAN_NOUT | AF_CONTROL_GET:
- *(int*)arg = af->data->nch;
- return AF_OK;
case AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET:{
float val = *(float*)arg;
if (s->nch)
@@ -181,7 +185,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data)
// Set output data
c->audio = l->audio;
c->len = c->len / c->nch * l->nch;
- c->nch = l->nch;
+ set_channels(c, l->nch);
return c;
}
diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c
index cf326fedfb..5cf0f3b082 100644
--- a/audio/filter/af_scaletempo.c
+++ b/audio/filter/af_scaletempo.c
@@ -302,26 +302,22 @@ static int control(struct af_instance* af, int cmd, void* arg)
"[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
s->speed, s->scale_nominal, s->scale);
+ mp_audio_copy_config(af->data, data);
+
if (s->scale == 1.0) {
if (s->speed_tempo && s->speed_pitch)
return AF_DETACH;
- memcpy(af->data, data, sizeof(struct mp_audio));
af->delay = 0;
af->mul = 1;
return af_test_output(af, data);
}
- af->data->rate = data->rate;
- af->data->nch = data->nch;
- if ( data->format == AF_FORMAT_S16_LE
- || data->format == AF_FORMAT_S16_BE ) {
+ if (data->format == AF_FORMAT_S16_NE) {
use_int = 1;
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = bps = 2;
} else {
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = bps = 4;
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
}
+ bps = af->data->bps;
frames_stride = srate * s->ms_stride;
s->bytes_stride = frames_stride * bps * nch;
diff --git a/audio/filter/af_sinesuppress.c b/audio/filter/af_sinesuppress.c
index 36f7189f00..10f0b650ec 100644
--- a/audio/filter/af_sinesuppress.c
+++ b/audio/filter/af_sinesuppress.c
@@ -54,8 +54,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Sanity check
if(!arg) return AF_ERROR;
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = 1;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_num_channels(af->data, 1);
#if 0
if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE)
{
@@ -65,8 +65,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
}// else
#endif
{
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
+ mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
af->play = play_s16;
}
@@ -79,18 +78,6 @@ static int control(struct af_instance* af, int cmd, void* arg)
s->decay = f2;
return AF_OK;
}
- case AF_CONTROL_SS_FREQ | AF_CONTROL_SET:
- s->freq = *(float*)arg;
- return AF_OK;
- case AF_CONTROL_SS_FREQ | AF_CONTROL_GET:
- *(float*)arg = s->freq;
- return AF_OK;
- case AF_CONTROL_SS_DECAY | AF_CONTROL_SET:
- s->decay = *(float*)arg;
- return AF_OK;
- case AF_CONTROL_SS_DECAY | AF_CONTROL_GET:
- *(float*)arg = s->decay;
- return AF_OK;
}
return AF_UNKNOWN;
}
diff --git a/audio/filter/af_sub.c b/audio/filter/af_sub.c
index 4af28d9141..a985ac2a05 100644
--- a/audio/filter/af_sub.c
+++ b/audio/filter/af_sub.c
@@ -70,9 +70,8 @@ static int control(struct af_instance* af, int cmd, void* arg)
if(!arg) return AF_ERROR;
af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch);
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_set_channels_old(af->data, max(s->ch+1,((struct mp_audio*)arg)->nch));
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
// Design low-pass filter
s->k = 1.0;
diff --git a/audio/filter/af_surround.c b/audio/filter/af_surround.c
index 57288d6ba2..c04a039d65 100644
--- a/audio/filter/af_surround.c
+++ b/audio/filter/af_surround.c
@@ -92,10 +92,9 @@ static int control(struct af_instance* af, int cmd, void* arg)
switch(cmd){
case AF_CONTROL_REINIT:{
float fc;
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch*2;
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
+ mp_audio_set_channels_old(af->data, ((struct mp_audio*)arg)->nch*2);
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
if (af->data->nch != 4){
mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Only stereo input is supported.\n");
@@ -125,8 +124,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
if((af->data->format != ((struct mp_audio*)arg)->format) ||
(af->data->bps != ((struct mp_audio*)arg)->bps)){
- ((struct mp_audio*)arg)->format = af->data->format;
- ((struct mp_audio*)arg)->bps = af->data->bps;
+ mp_audio_set_format((struct mp_audio*)arg, af->data->format);
return AF_FALSE;
}
return AF_OK;
@@ -244,7 +242,7 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){
// Set output data
data->audio = af->data->audio;
data->len *= 2;
- data->nch = af->data->nch;
+ mp_audio_set_channels_old(data, af->data->nch);
return data;
}
diff --git a/audio/filter/af_sweep.c b/audio/filter/af_sweep.c
index 6d1106fefc..6cc099f2d8 100644
--- a/audio/filter/af_sweep.c
+++ b/audio/filter/af_sweep.c
@@ -41,12 +41,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
switch(cmd){
case AF_CONTROL_REINIT:
- af->data->nch = data->nch;
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
- af->data->rate = data->rate;
+ mp_audio_copy_config(af->data, data);
+ mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
- return AF_OK;
+ return af_test_output(af, data);
case AF_CONTROL_COMMAND_LINE:
sscanf((char*)arg,"%lf", &s->delta);
return AF_OK;
diff --git a/audio/filter/af_tools.c b/audio/filter/af_tools.c
index 0d5dc6c573..77fdad55f2 100644
--- a/audio/filter/af_tools.c
+++ b/audio/filter/af_tools.c
@@ -90,8 +90,8 @@ int af_test_output(struct af_instance* af, struct mp_audio* out)
if((af->data->format != out->format) ||
(af->data->bps != out->bps) ||
(af->data->rate != out->rate) ||
- (af->data->nch != out->nch)){
- memcpy(out,af->data,sizeof(struct mp_audio));
+ !mp_chmap_equals(&af->data->channels, &out->channels)){
+ *out = *af->data;
return AF_FALSE;
}
return AF_OK;
diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c
index ecf181c8b8..82c31eaa12 100644
--- a/audio/filter/af_volume.c
+++ b/audio/filter/af_volume.c
@@ -66,12 +66,10 @@ static int control(struct af_instance* af, int cmd, void* arg)
// Sanity check
if(!arg) return AF_ERROR;
- af->data->rate = ((struct mp_audio*)arg)->rate;
- af->data->nch = ((struct mp_audio*)arg)->nch;
+ mp_audio_copy_config(af->data, (struct mp_audio*)arg);
if(s->fast && (((struct mp_audio*)arg)->format != (AF_FORMAT_FLOAT_NE))){
- af->data->format = AF_FORMAT_S16_NE;
- af->data->bps = 2;
+ mp_audio_set_format(af->data, AF_FORMAT_S16_NE);
}
else{
// Cutoff set to 10Hz for forgetting factor
@@ -79,42 +77,21 @@ static int control(struct af_instance* af, int cmd, void* arg)
float t = 2.0-cos(x);
s->time = 1.0 - (t - sqrt(t*t - 1));
mp_msg(MSGT_AFILTER, MSGL_DBG2, "[volume] Forgetting factor = %0.5f\n",s->time);
- af->data->format = AF_FORMAT_FLOAT_NE;
- af->data->bps = 4;
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT_NE);
}
return af_test_output(af,(struct mp_audio*)arg);
case AF_CONTROL_COMMAND_LINE:{
float v=0.0;
float vol[AF_NCH];
int i;
- sscanf((char*)arg,"%f:%i", &v, &s->soft);
+ sscanf((char*)arg,"%f:%i:%i", &v, &s->soft, &s->fast);
for(i=0;i<AF_NCH;i++) vol[i]=v;
return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol);
}
- case AF_CONTROL_POST_CREATE:
- s->fast = ((((struct af_cfg*)arg)->force & AF_INIT_FORMAT_MASK) ==
- AF_INIT_FLOAT) ? 0 : 1;
- return AF_OK;
- case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_SET:
- memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int));
- return AF_OK;
- case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_GET:
- memcpy((int*)arg,s->enable,AF_NCH*sizeof(int));
- return AF_OK;
- case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET:
- s->soft = *(int*)arg;
- return AF_OK;
- case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_GET:
- *(int*)arg = s->soft;
- return AF_OK;
case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET:
return af_from_dB(AF_NCH,(float*)arg,s->level,20.0,-200.0,60.0);
case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET:
return af_to_dB(AF_NCH,s->level,(float*)arg,20.0);
- case AF_CONTROL_VOLUME_PROBE | AF_CONTROL_GET:
- return af_to_dB(AF_NCH,s->pow,(float*)arg,10.0);
- case AF_CONTROL_VOLUME_PROBE_MAX | AF_CONTROL_GET:
- return af_to_dB(AF_NCH,s->max,(float*)arg,10.0);
case AF_CONTROL_PRE_DESTROY:{
float m = 0.0;
int i;
@@ -122,7 +99,7 @@ static int control(struct af_instance* af, int cmd, void* arg)
for(i=0;i<AF_NCH;i++)
m=max(m,s->max[i]);
af_to_dB(1, &m, &m, 10.0);
- mp_msg(MSGT_AFILTER, MSGL_INFO, "[volume] The maximum volume was %0.2fdB \n", m);
+ mp_msg(MSGT_AFILTER, MSGL_V, "[volume] The maximum volume was %0.2fdB \n", m);
}
return AF_OK;
}
diff --git a/audio/filter/control.h b/audio/filter/control.h
index 323b9a3924..aa1900d106 100644
--- a/audio/filter/control.h
+++ b/audio/filter/control.h
@@ -22,48 +22,6 @@
#include <sys/types.h>
/*********************************************
-// Control info struct.
-//
-// This struct is the argument in a info call to a filter.
-*/
-
-// Argument types
-#define AF_CONTROL_TYPE_BOOL (0x0<<0)
-#define AF_CONTROL_TYPE_CHAR (0x1<<0)
-#define AF_CONTROL_TYPE_INT (0x2<<0)
-#define AF_CONTROL_TYPE_FLOAT (0x3<<0)
-#define AF_CONTROL_TYPE_STRUCT (0x4<<0)
-#define AF_CONTROL_TYPE_SPECIAL (0x5<<0) // a pointer to a function for example
-#define AF_CONTROL_TYPE_MASK (0x7<<0)
-// Argument geometry
-#define AF_CONTROL_GEOM_SCALAR (0x0<<3)
-#define AF_CONTROL_GEOM_ARRAY (0x1<<3)
-#define AF_CONTROL_GEOM_MATRIX (0x2<<3)
-#define AF_CONTROL_GEOM_MASK (0x3<<3)
-// Argument properties
-#define AF_CONTROL_PROP_READ (0x0<<5) // The argument can be read
-#define AF_CONTROL_PROP_WRITE (0x1<<5) // The argument can be written
-#define AF_CONTROL_PROP_SAVE (0x2<<5) // Can be saved
-#define AF_CONTROL_PROP_RUNTIME (0x4<<5) // Acessable during execution
-#define AF_CONTROL_PROP_CHANNEL (0x8<<5) // Argument is set per channel
-#define AF_CONTROL_PROP_MASK (0xF<<5)
-
-typedef struct af_control_info_s{
- int def; // Control enumrification
- char* name; // Name of argument
- char* info; // Description of what it does
- int flags; // Flags as defined above
- float max; // Max and min value
- float min; // (only aplicable on float and int)
- int xdim; // 1st dimension
- int ydim; // 2nd dimension (=0 for everything except matrix)
- size_t sz; // Size of argument in bytes
- int ch; // Channel number (for future use)
- void* arg; // Data (for future use)
-}af_control_info_t;
-
-
-/*********************************************
// Extended control used with arguments that operates on only one
// channel at the time
*/
@@ -98,11 +56,6 @@ typedef struct af_control_ext_s{
// OPTIONAL CALLS
-/* Called just after creation with the af_cfg for the stream in which
- the filter resides as input parameter this call can be used by the
- filter to initialize itself */
-#define AF_CONTROL_POST_CREATE 0x00000100 | AF_CONTROL_OPTIONAL
-
// Called just before destruction of a filter
#define AF_CONTROL_PRE_DESTROY 0x00000200 | AF_CONTROL_OPTIONAL
@@ -119,20 +72,12 @@ typedef struct af_control_ext_s{
#define AF_CONTROL_SET 0x00000000
// Get argument
#define AF_CONTROL_GET 0x00000001
-// Get info about the control, i.e fill in everything except argument
-#define AF_CONTROL_INFO 0x00000002
// Resample
// Set output rate in resample
#define AF_CONTROL_RESAMPLE_RATE 0x00000100 | AF_CONTROL_FILTER_SPECIFIC
-// Enable sloppy resampling
-#define AF_CONTROL_RESAMPLE_SLOPPY 0x00000200 | AF_CONTROL_FILTER_SPECIFIC
-
-// Set resampling accuracy
-#define AF_CONTROL_RESAMPLE_ACCURACY 0x00000300 | AF_CONTROL_FILTER_SPECIFIC
-
// Format
#define AF_CONTROL_FORMAT_FMT 0x00000400 | AF_CONTROL_FILTER_SPECIFIC
@@ -142,69 +87,11 @@ typedef struct af_control_ext_s{
// Set number of output channels in channels
#define AF_CONTROL_CHANNELS 0x00000600 | AF_CONTROL_FILTER_SPECIFIC
-// Set number of channel routes
-#define AF_CONTROL_CHANNELS_ROUTES 0x00000700 | AF_CONTROL_FILTER_SPECIFIC
-
-// Set channel routing pair, arg is int[2] and ch is used
-#define AF_CONTROL_CHANNELS_ROUTING 0x00000800 | AF_CONTROL_FILTER_SPECIFIC
-
-// Set nuber of channel routing pairs, arg is int*
-#define AF_CONTROL_CHANNELS_NR 0x00000900 | AF_CONTROL_FILTER_SPECIFIC
-
-// Set make af_channels into a router
-#define AF_CONTROL_CHANNELS_ROUTER 0x00000A00 | AF_CONTROL_FILTER_SPECIFIC
-
// Volume
-// Turn volume control on and off, arg is int*
-#define AF_CONTROL_VOLUME_ON_OFF 0x00000B00 | AF_CONTROL_FILTER_SPECIFIC
-
-// Turn soft clipping of the volume on and off, arg is binary
-#define AF_CONTROL_VOLUME_SOFTCLIP 0x00000C00 | AF_CONTROL_FILTER_SPECIFIC
-
// Set volume level, arg is a float* with the volume for all the channels
#define AF_CONTROL_VOLUME_LEVEL 0x00000D00 | AF_CONTROL_FILTER_SPECIFIC
-// Probed power level for all channels, arg is a float*
-#define AF_CONTROL_VOLUME_PROBE 0x00000E00 | AF_CONTROL_FILTER_SPECIFIC
-
-// Maximum probed power level for all channels, arg is a float*
-#define AF_CONTROL_VOLUME_PROBE_MAX 0x00000F00 | AF_CONTROL_FILTER_SPECIFIC
-
-// Compressor/expander
-
-// Turn compressor/expander on and off
-#define AF_CONTROL_COMP_ON_OFF 0x00001000 | AF_CONTROL_FILTER_SPECIFIC
-
-// Compression/expansion threshold [dB]
-#define AF_CONTROL_COMP_THRESH 0x00001100 | AF_CONTROL_FILTER_SPECIFIC
-
-// Compression/expansion attack time [ms]
-#define AF_CONTROL_COMP_ATTACK 0x00001200 | AF_CONTROL_FILTER_SPECIFIC
-
-// Compression/expansion release time [ms]
-#define AF_CONTROL_COMP_RELEASE 0x00001300 | AF_CONTROL_FILTER_SPECIFIC
-
-// Compression/expansion gain level [dB]
-#define AF_CONTROL_COMP_RATIO 0x00001400 | AF_CONTROL_FILTER_SPECIFIC
-
-// Noise gate
-
-// Turn noise gate on an off
-#define AF_CONTROL_GATE_ON_OFF 0x00001500 | AF_CONTROL_FILTER_SPECIFIC
-
-// Noise gate threshold [dB]
-#define AF_CONTROL_GATE_THRESH 0x00001600 | AF_CONTROL_FILTER_SPECIFIC
-
-// Noise gate attack time [ms]
-#define AF_CONTROL_GATE_ATTACK 0x00001700 | AF_CONTROL_FILTER_SPECIFIC
-
-// Noise gate release time [ms]
-#define AF_CONTROL_GATE_RELEASE 0x00001800 | AF_CONTROL_FILTER_SPECIFIC
-
-// Noise gate release range level [dB]
-#define AF_CONTROL_GATE_RANGE 0x00001900 | AF_CONTROL_FILTER_SPECIFIC
-
// Pan
// Pan levels, arg is a control_ext with a float*
@@ -216,9 +103,6 @@ typedef struct af_control_ext_s{
// Balance, arg is float*; range -1 (left) to 1 (right), 0 center
#define AF_CONTROL_PAN_BALANCE 0x00001C00 | AF_CONTROL_FILTER_SPECIFIC
-// Set equalizer gain, arg is a control_ext with a float*
-#define AF_CONTROL_EQUALIZER_GAIN 0x00001D00 | AF_CONTROL_FILTER_SPECIFIC
-
// Delay length in ms, arg is a control_ext with a float*
#define AF_CONTROL_DELAY_LEN 0x00001E00 | AF_CONTROL_FILTER_SPECIFIC
@@ -236,21 +120,10 @@ typedef struct af_control_ext_s{
// Export
#define AF_CONTROL_EXPORT_SZ 0x00003000 | AF_CONTROL_FILTER_SPECIFIC
-
-// ExtraStereo Multiplier
-#define AF_CONTROL_ES_MUL 0x00003100 | AF_CONTROL_FILTER_SPECIFIC
-
-
-// Center
-
// Channel number which to inster the filtered data, arg in int*
#define AF_CONTROL_CENTER_CH 0x00003200 | AF_CONTROL_FILTER_SPECIFIC
-// SineSuppress
-#define AF_CONTROL_SS_FREQ 0x00003300 | AF_CONTROL_FILTER_SPECIFIC
-#define AF_CONTROL_SS_DECAY 0x00003400 | AF_CONTROL_FILTER_SPECIFIC
-
#define AF_CONTROL_PLAYBACK_SPEED 0x00003500 | AF_CONTROL_FILTER_SPECIFIC
#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00003600 | AF_CONTROL_FILTER_SPECIFIC
diff --git a/audio/fmt-conversion.c b/audio/fmt-conversion.c
new file mode 100644
index 0000000000..4c1055f118
--- /dev/null
+++ b/audio/fmt-conversion.c
@@ -0,0 +1,65 @@
+/*
+ * 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 "core/mp_msg.h"
+#include <libavutil/avutil.h>
+#include <libavutil/samplefmt.h>
+#include "format.h"
+#include "fmt-conversion.h"
+
+static const struct {
+ enum AVSampleFormat sample_fmt;
+ int fmt;
+} audio_conversion_map[] = {
+ {AV_SAMPLE_FMT_U8, AF_FORMAT_U8},
+ {AV_SAMPLE_FMT_S16, AF_FORMAT_S16_NE},
+ {AV_SAMPLE_FMT_S32, AF_FORMAT_S32_NE},
+ {AV_SAMPLE_FMT_FLT, AF_FORMAT_FLOAT_NE},
+ {AV_SAMPLE_FMT_DBL, AF_FORMAT_DOUBLE_NE},
+
+ {AV_SAMPLE_FMT_NONE, 0},
+};
+
+enum AVSampleFormat af_to_avformat(int fmt)
+{
+ int i;
+ enum AVSampleFormat sample_fmt;
+ for (i = 0; audio_conversion_map[i].fmt; i++)
+ if (audio_conversion_map[i].fmt == fmt)
+ break;
+ sample_fmt = audio_conversion_map[i].sample_fmt;
+ if (sample_fmt == AF_FORMAT_UNKNOWN)
+ mp_msg(MSGT_GLOBAL, MSGL_V, "Unsupported sample format: %s\n",
+ af_fmt2str_short(fmt));
+ return sample_fmt;
+}
+
+int af_from_avformat(enum AVSampleFormat sample_fmt)
+{
+ int i;
+ for (i = 0; audio_conversion_map[i].fmt; i++)
+ if (audio_conversion_map[i].sample_fmt == sample_fmt)
+ break;
+ int fmt = audio_conversion_map[i].fmt;
+ if (!fmt) {
+ const char *fmtname = av_get_sample_fmt_name(sample_fmt);
+ mp_msg(MSGT_GLOBAL, MSGL_ERR, "Unsupported AVSampleFormat %s (%d)\n",
+ fmtname ? fmtname : "INVALID", sample_fmt);
+ }
+ return fmt;
+}
diff --git a/audio/fmt-conversion.h b/audio/fmt-conversion.h
new file mode 100644
index 0000000000..7f2739f86f
--- /dev/null
+++ b/audio/fmt-conversion.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef MPLAYER_SAMPLE_FMT_CONVERSION_H
+#define MPLAYER_SAMPLE_FMT_CONVERSION_H
+
+enum AVSampleFormat af_to_avformat(int fmt);
+int af_from_avformat(enum AVSampleFormat sample_fmt);
+
+#endif /* MPLAYER_SAMPLE_FMT_CONVERSION_H */
diff --git a/audio/format.c b/audio/format.c
index 9625857ada..5b1262956c 100644
--- a/audio/format.c
+++ b/audio/format.c
@@ -29,24 +29,17 @@
int af_fmt2bits(int format)
{
if (AF_FORMAT_IS_AC3(format)) return 16;
- return (format & AF_FORMAT_BITS_MASK)+8;
-// return (((format & AF_FORMAT_BITS_MASK)>>3)+1) * 8;
-#if 0
+ if (format == AF_FORMAT_UNKNOWN)
+ return 0;
switch(format & AF_FORMAT_BITS_MASK)
{
case AF_FORMAT_8BIT: return 8;
case AF_FORMAT_16BIT: return 16;
case AF_FORMAT_24BIT: return 24;
case AF_FORMAT_32BIT: return 32;
- case AF_FORMAT_48BIT: return 48;
+ case AF_FORMAT_64BIT: return 64;
}
-#endif
- return -1;
-}
-
-int af_bits2fmt(int bits)
-{
- return (bits/8 - 1) << 3;
+ return 0;
}
/* Convert format to str input str is a buffer for the
@@ -94,6 +87,9 @@ const struct af_fmt_entry af_fmtstr_table[] = {
{ "floatle", AF_FORMAT_FLOAT_LE },
{ "floatbe", AF_FORMAT_FLOAT_BE },
{ "floatne", AF_FORMAT_FLOAT_NE },
+ { "doublele", AF_FORMAT_DOUBLE_LE },
+ { "doublebe", AF_FORMAT_DOUBLE_BE },
+ { "doublene", AF_FORMAT_DOUBLE_NE },
{0}
};
diff --git a/audio/format.h b/audio/format.h
index a8249954f0..30a4aa1cea 100644
--- a/audio/format.h
+++ b/audio/format.h
@@ -53,8 +53,7 @@
#define AF_FORMAT_16BIT (1<<3)
#define AF_FORMAT_24BIT (2<<3)
#define AF_FORMAT_32BIT (3<<3)
-#define AF_FORMAT_40BIT (4<<3)
-#define AF_FORMAT_48BIT (5<<3)
+#define AF_FORMAT_64BIT (4<<3)
#define AF_FORMAT_BITS_MASK (7<<3)
// Special flags refering to non pcm data (note: 1<<6, 2<<6, 5<<6 unused)
@@ -85,6 +84,9 @@
#define AF_FORMAT_FLOAT_LE (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE)
#define AF_FORMAT_FLOAT_BE (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE)
+#define AF_FORMAT_DOUBLE_LE (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE)
+#define AF_FORMAT_DOUBLE_BE (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_BE)
+
#define AF_FORMAT_AC3_LE (AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE)
#define AF_FORMAT_AC3_BE (AF_FORMAT_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE)
@@ -99,6 +101,7 @@
#define AF_FORMAT_U32_NE AF_FORMAT_U32_BE
#define AF_FORMAT_S32_NE AF_FORMAT_S32_BE
#define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_BE
+#define AF_FORMAT_DOUBLE_NE AF_FORMAT_DOUBLE_BE
#define AF_FORMAT_AC3_NE AF_FORMAT_AC3_BE
#define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_BE
#else
@@ -109,6 +112,7 @@
#define AF_FORMAT_U32_NE AF_FORMAT_U32_LE
#define AF_FORMAT_S32_NE AF_FORMAT_S32_LE
#define AF_FORMAT_FLOAT_NE AF_FORMAT_FLOAT_LE
+#define AF_FORMAT_DOUBLE_NE AF_FORMAT_DOUBLE_LE
#define AF_FORMAT_AC3_NE AF_FORMAT_AC3_LE
#define AF_FORMAT_IEC61937_NE AF_FORMAT_IEC61937_LE
#endif
@@ -127,7 +131,6 @@ extern const struct af_fmt_entry af_fmtstr_table[];
int af_str2fmt_short(bstr str);
int af_fmt2bits(int format);
-int af_bits2fmt(int bits);
char* af_fmt2str(int format, char* str, int size);
const char* af_fmt2str_short(int format);
diff --git a/audio/mixer.c b/audio/mixer.c
index b67e548a3c..0f1a7871d6 100644
--- a/audio/mixer.c
+++ b/audio/mixer.c
@@ -217,7 +217,7 @@ void mixer_setbalance(mixer_t *mixer, float val)
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val))
return;
- if (val == 0 || mixer->ao->channels < 2)
+ if (val == 0 || mixer->ao->channels.num < 2)
return;
if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) {
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 9fb201a333..10badcfa07 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -250,14 +250,24 @@ void ao_resume(struct ao *ao)
ao->driver->resume(ao);
}
+bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map)
+{
+ return mp_chmap_sel_adjust(s, map);
+}
+bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map, int num)
+{
+ return mp_chmap_sel_get_def(s, map, num);
+}
int old_ao_init(struct ao *ao, char *params)
{
assert(!global_ao);
global_ao = ao;
ao_subdevice = params ? talloc_strdup(ao, params) : NULL;
- if (ao->driver->old_functions->init(ao->samplerate, ao->channels,
+ if (ao->driver->old_functions->init(ao->samplerate, &ao->channels,
ao->format, 0) == 0) {
global_ao = NULL;
return -1;
diff --git a/audio/out/ao.h b/audio/out/ao.h
index 4a7c928824..d908841457 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -23,6 +23,8 @@
#include "core/bstr.h"
#include "core/mp_common.h"
+#include "audio/chmap.h"
+#include "audio/chmap_sel.h"
enum aocontrol {
// _VOLUME commands take struct ao_control_vol pointer for input/output.
@@ -55,7 +57,7 @@ typedef struct ao_info {
/* interface towards mplayer and */
typedef struct ao_old_functions {
int (*control)(int cmd, void *arg);
- int (*init)(int rate, int channels, int format, int flags);
+ int (*init)(int rate, const struct mp_chmap *channels, int format, int flags);
void (*uninit)(int immed);
void (*reset)(void);
int (*get_space)(void);
@@ -68,7 +70,6 @@ typedef struct ao_old_functions {
struct ao;
struct ao_driver {
- bool is_new;
bool encode;
const struct ao_info *info;
const struct ao_old_functions *old_functions;
@@ -86,9 +87,9 @@ struct ao_driver {
/* global data used by mplayer and plugins */
struct ao {
int samplerate;
- int channels;
+ struct mp_chmap channels;
int format;
- int bps;
+ int bps; // bytes per second
int outburst;
int buffersize;
double pts;
@@ -121,6 +122,11 @@ void ao_reset(struct ao *ao);
void ao_pause(struct ao *ao);
void ao_resume(struct ao *ao);
+bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map);
+bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map, int num);
+
int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
int old_ao_init(struct ao *ao, char *params);
void old_ao_uninit(struct ao *ao, bool cut_audio);
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index 366b912b76..93327881e5 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -36,6 +36,7 @@
#include <alloca.h>
#include "config.h"
+#include "core/options.h"
#include "core/subopt-helper.h"
#include "audio/mixer.h"
#include "core/mp_msg.h"
@@ -46,292 +47,372 @@
#include <alsa/asoundlib.h>
#include "ao.h"
-#include "audio_out_internal.h"
#include "audio/format.h"
-
-static const ao_info_t info =
-{
- "ALSA-0.9.x-1.x audio output",
- "alsa",
- "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>",
- "under development"
+#include "audio/reorder_ch.h"
+
+struct priv {
+ snd_pcm_t *alsa;
+ snd_pcm_format_t alsa_fmt;
+ size_t bytes_per_sample;
+ int can_pause;
+ snd_pcm_sframes_t prepause_frames;
+ float delay_before_pause;
};
-LIBAO_EXTERN(alsa)
-
-static snd_pcm_t *alsa_handler;
-static snd_pcm_format_t alsa_format;
-
#define BUFFER_TIME 500000 // 0.5 s
#define FRAGCOUNT 16
-static size_t bytes_per_sample;
+#define ALSA_DEVICE_SIZE 256
-static int alsa_can_pause;
-static snd_pcm_sframes_t prepause_frames;
-static float delay_before_pause;
+#define CHECK_ALSA_ERROR(message) \
+ do { \
+ if (err < 0) { \
+ mp_msg(MSGT_VO, MSGL_ERR, "[AO_ALSA] %s: %s\n", \
+ (message), snd_strerror(err)); \
+ goto alsa_error; \
+ } \
+ } while (0)
-#define ALSA_DEVICE_SIZE 256
+static float get_delay(struct ao *ao);
+static int play(struct ao *ao, void *data, int len, int flags);
static void alsa_error_handler(const char *file, int line, const char *function,
- int err, const char *format, ...)
+ int err, const char *format, ...)
{
- char tmp[0xc00];
- va_list va;
-
- va_start(va, format);
- vsnprintf(tmp, sizeof tmp, format, va);
- va_end(va);
-
- if (err)
- mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n",
- file, line, function, tmp, snd_strerror(err));
- else
- mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n",
- file, line, function, tmp);
+ char tmp[0xc00];
+ va_list va;
+
+ va_start(va, format);
+ vsnprintf(tmp, sizeof tmp, format, va);
+ va_end(va);
+
+ if (err) {
+ mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n",
+ file, line, function, tmp, snd_strerror(err));
+ } else {
+ mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n",
+ file, line, function, tmp);
+ }
}
/* to set/get/query special features/parameters */
-static int control(int cmd, void *arg)
+static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
- switch(cmd) {
- case AOCONTROL_GET_MUTE:
- case AOCONTROL_SET_MUTE:
- case AOCONTROL_GET_VOLUME:
- case AOCONTROL_SET_VOLUME:
+ snd_mixer_t *handle = NULL;
+ switch (cmd) {
+ case AOCONTROL_GET_MUTE:
+ case AOCONTROL_SET_MUTE:
+ case AOCONTROL_GET_VOLUME:
+ case AOCONTROL_SET_VOLUME:
{
- int err;
- snd_mixer_t *handle;
- snd_mixer_elem_t *elem;
- snd_mixer_selem_id_t *sid;
-
- char *mix_name = "Master";
- char *card = "default";
- int mix_index = 0;
-
- long pmin, pmax;
- long get_vol, set_vol;
- float f_multi;
-
- if(AF_FORMAT_IS_IEC61937(ao_data.format))
- return CONTROL_TRUE;
-
- if(mixer_channel) {
- char *test_mix_index;
-
- mix_name = strdup(mixer_channel);
- if ((test_mix_index = strchr(mix_name, ','))){
- *test_mix_index = 0;
- test_mix_index++;
- mix_index = strtol(test_mix_index, &test_mix_index, 0);
-
- if (*test_mix_index){
- mp_tmsg(MSGT_AO,MSGL_ERR,
- "[AO_ALSA] Invalid mixer index. Defaulting to 0.\n");
- mix_index = 0 ;
- }
- }
- }
- if(mixer_device) card = mixer_device;
-
- //allocate simple id
- snd_mixer_selem_id_alloca(&sid);
-
- //sets simple-mixer index and name
- snd_mixer_selem_id_set_index(sid, mix_index);
- snd_mixer_selem_id_set_name(sid, mix_name);
-
- if (mixer_channel) {
- free(mix_name);
- mix_name = NULL;
- }
-
- if ((err = snd_mixer_open(&handle, 0)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer open error: %s\n", snd_strerror(err));
- return CONTROL_ERROR;
- }
-
- if ((err = snd_mixer_attach(handle, card)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer attach %s error: %s\n",
- card, snd_strerror(err));
- snd_mixer_close(handle);
- return CONTROL_ERROR;
- }
-
- if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer register error: %s\n", snd_strerror(err));
- snd_mixer_close(handle);
- return CONTROL_ERROR;
- }
- err = snd_mixer_load(handle);
- if (err < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer load error: %s\n", snd_strerror(err));
- snd_mixer_close(handle);
- return CONTROL_ERROR;
- }
-
- elem = snd_mixer_find_selem(handle, sid);
- if (!elem) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to find simple control '%s',%i.\n",
- snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
- snd_mixer_close(handle);
- return CONTROL_ERROR;
- }
-
- snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
- f_multi = (100 / (float)(pmax - pmin));
-
- switch (cmd) {
- case AOCONTROL_SET_VOLUME: {
- ao_control_vol_t *vol = arg;
- set_vol = vol->left / f_multi + pmin + 0.5;
-
- //setting channels
- if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting left channel, %s\n",
- snd_strerror(err));
- goto mixer_error;
- }
- mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol);
-
- set_vol = vol->right / f_multi + pmin + 0.5;
-
- if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting right channel, %s\n",
- snd_strerror(err));
- goto mixer_error;
- }
- mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n",
- set_vol, pmin, pmax, f_multi);
- break;
- }
- case AOCONTROL_GET_VOLUME: {
- ao_control_vol_t *vol = arg;
- snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol);
- vol->left = (get_vol - pmin) * f_multi;
- snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol);
- vol->right = (get_vol - pmin) * f_multi;
- mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right);
- break;
- }
- case AOCONTROL_SET_MUTE: {
- bool *mute = arg;
- if (!snd_mixer_selem_has_playback_switch(elem))
- goto mixer_error;
- if (!snd_mixer_selem_has_playback_switch_joined(elem)) {
- snd_mixer_selem_set_playback_switch(
- elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute);
- }
- snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT,
- !*mute);
- break;
- }
- case AOCONTROL_GET_MUTE: {
- bool *mute = arg;
- if (!snd_mixer_selem_has_playback_switch(elem))
- goto mixer_error;
- int tmp = 1;
- snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT,
- &tmp);
- *mute = !tmp;
- if (!snd_mixer_selem_has_playback_switch_joined(elem)) {
- snd_mixer_selem_get_playback_switch(
- elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp);
- *mute &= !tmp;
+ int err;
+ snd_mixer_elem_t *elem;
+ snd_mixer_selem_id_t *sid;
+
+ char *mix_name = "Master";
+ char *card = "default";
+ int mix_index = 0;
+
+ long pmin, pmax;
+ long get_vol, set_vol;
+ float f_multi;
+
+ if (AF_FORMAT_IS_IEC61937(ao->format))
+ return CONTROL_TRUE;
+
+ if (ao->opts->mixer_channel) {
+ char *test_mix_index;
+
+ mix_name = strdup(ao->opts->mixer_channel);
+ if ((test_mix_index = strchr(mix_name, ','))) {
+ *test_mix_index = 0;
+ test_mix_index++;
+ mix_index = strtol(test_mix_index, &test_mix_index, 0);
+
+ if (*test_mix_index) {
+ mp_tmsg(MSGT_AO, MSGL_ERR,
+ "[AO_ALSA] Invalid mixer index. Defaulting to 0.\n");
+ mix_index = 0;
+ }
+ }
+ }
+ if (ao->opts->mixer_device)
+ card = ao->opts->mixer_device;
+
+ //allocate simple id
+ snd_mixer_selem_id_alloca(&sid);
+
+ //sets simple-mixer index and name
+ snd_mixer_selem_id_set_index(sid, mix_index);
+ snd_mixer_selem_id_set_name(sid, mix_name);
+
+ if (ao->opts->mixer_channel) {
+ free(mix_name);
+ mix_name = NULL;
}
- break;
- }
- }
- snd_mixer_close(handle);
- return CONTROL_OK;
- mixer_error:
- snd_mixer_close(handle);
- return CONTROL_ERROR;
+
+ err = snd_mixer_open(&handle, 0);
+ CHECK_ALSA_ERROR("Mixer open error");
+
+ err = snd_mixer_attach(handle, card);
+ CHECK_ALSA_ERROR("Mixer attach error");
+
+ err = snd_mixer_selem_register(handle, NULL, NULL);
+ CHECK_ALSA_ERROR("Mixer register error");
+
+ err = snd_mixer_load(handle);
+ CHECK_ALSA_ERROR("Mixer load error");
+
+ elem = snd_mixer_find_selem(handle, sid);
+ if (!elem) {
+ mp_tmsg(MSGT_AO, MSGL_ERR,
+ "[AO_ALSA] Unable to find simple control '%s',%i.\n",
+ snd_mixer_selem_id_get_name(sid),
+ snd_mixer_selem_id_get_index(sid));
+ goto alsa_error;
+ }
+
+ snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
+ f_multi = (100 / (float)(pmax - pmin));
+
+ switch (cmd) {
+ case AOCONTROL_SET_VOLUME: {
+ ao_control_vol_t *vol = arg;
+ set_vol = vol->left / f_multi + pmin + 0.5;
+
+ //setting channels
+ err = snd_mixer_selem_set_playback_volume
+ (elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol);
+ CHECK_ALSA_ERROR("Error setting left channel");
+ mp_msg(MSGT_AO, MSGL_DBG2, "left=%li, ", set_vol);
+
+ set_vol = vol->right / f_multi + pmin + 0.5;
+
+ err = snd_mixer_selem_set_playback_volume
+ (elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol);
+ CHECK_ALSA_ERROR("Error setting right channel");
+ mp_msg(MSGT_AO, MSGL_DBG2,
+ "right=%li, pmin=%li, pmax=%li, mult=%f\n",
+ set_vol, pmin, pmax,
+ f_multi);
+ break;
+ }
+ case AOCONTROL_GET_VOLUME: {
+ ao_control_vol_t *vol = arg;
+ snd_mixer_selem_get_playback_volume
+ (elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol);
+ vol->left = (get_vol - pmin) * f_multi;
+ snd_mixer_selem_get_playback_volume
+ (elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol);
+ vol->right = (get_vol - pmin) * f_multi;
+ mp_msg(MSGT_AO, MSGL_DBG2, "left=%f, right=%f\n", vol->left,
+ vol->right);
+ break;
+ }
+ case AOCONTROL_SET_MUTE: {
+ bool *mute = arg;
+ if (!snd_mixer_selem_has_playback_switch(elem))
+ goto alsa_error;
+ if (!snd_mixer_selem_has_playback_switch_joined(elem)) {
+ snd_mixer_selem_set_playback_switch
+ (elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute);
+ }
+ snd_mixer_selem_set_playback_switch
+ (elem, SND_MIXER_SCHN_FRONT_LEFT, !*mute);
+ break;
+ }
+ case AOCONTROL_GET_MUTE: {
+ bool *mute = arg;
+ if (!snd_mixer_selem_has_playback_switch(elem))
+ goto alsa_error;
+ int tmp = 1;
+ snd_mixer_selem_get_playback_switch
+ (elem, SND_MIXER_SCHN_FRONT_LEFT, &tmp);
+ *mute = !tmp;
+ if (!snd_mixer_selem_has_playback_switch_joined(elem)) {
+ snd_mixer_selem_get_playback_switch
+ (elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp);
+ *mute &= !tmp;
+ }
+ break;
+ }
+ }
+ snd_mixer_close(handle);
+ return CONTROL_OK;
}
- } //end switch
- return CONTROL_UNKNOWN;
+ } //end switch
+ return CONTROL_UNKNOWN;
+
+alsa_error:
+ if (handle)
+ snd_mixer_close(handle);
+ return CONTROL_ERROR;
}
-static void parse_device (char *dest, const char *src, int len)
+static void parse_device(char *dest, const char *src, int len)
{
- char *tmp;
- memmove(dest, src, len);
- dest[len] = 0;
- while ((tmp = strrchr(dest, '.')))
- tmp[0] = ',';
- while ((tmp = strrchr(dest, '=')))
- tmp[0] = ':';
+ char *tmp;
+ memmove(dest, src, len);
+ dest[len] = 0;
+ while ((tmp = strrchr(dest, '.')))
+ tmp[0] = ',';
+ while ((tmp = strrchr(dest, '=')))
+ tmp[0] = ':';
}
-static void print_help (void)
+static void print_help(void)
{
- mp_tmsg (MSGT_AO, MSGL_FATAL,
- "\n[AO_ALSA] -ao alsa commandline help:\n"\
- "[AO_ALSA] Example: mpv -ao alsa:device=hw=0.3\n"\
- "[AO_ALSA] Sets first card fourth hardware device.\n\n"\
- "[AO_ALSA] Options:\n"\
- "[AO_ALSA] noblock\n"\
- "[AO_ALSA] Opens device in non-blocking mode.\n"\
- "[AO_ALSA] device=<device-name>\n"\
- "[AO_ALSA] Sets device (change , to . and : to =)\n");
+ mp_tmsg(MSGT_AO, MSGL_FATAL,
+ "\n[AO_ALSA] -ao alsa commandline help:\n" \
+ "[AO_ALSA] Example: mpv -ao alsa:device=hw=0.3\n" \
+ "[AO_ALSA] Sets first card fourth hardware device.\n\n" \
+ "[AO_ALSA] Options:\n" \
+ "[AO_ALSA] noblock\n" \
+ "[AO_ALSA] Opens device in non-blocking mode.\n" \
+ "[AO_ALSA] device=<device-name>\n" \
+ "[AO_ALSA] Sets device (change , to . and : to =)\n");
}
-static int str_maxlen(void *strp) {
- strarg_t *str = strp;
- return str->len <= ALSA_DEVICE_SIZE;
+static int str_maxlen(void *strp)
+{
+ strarg_t *str = strp;
+ return str->len <= ALSA_DEVICE_SIZE;
}
-static int try_open_device(const char *device, int open_mode, int try_ac3)
+
+static const int mp_to_alsa_format[][2] = {
+ {AF_FORMAT_S8, SND_PCM_FORMAT_S8},
+ {AF_FORMAT_U8, SND_PCM_FORMAT_U8},
+ {AF_FORMAT_U16_LE, SND_PCM_FORMAT_U16_LE},
+ {AF_FORMAT_U16_BE, SND_PCM_FORMAT_U16_BE},
+ {AF_FORMAT_S16_LE, SND_PCM_FORMAT_S16_LE},
+ {AF_FORMAT_S16_BE, SND_PCM_FORMAT_S16_BE},
+ {AF_FORMAT_U32_LE, SND_PCM_FORMAT_U32_LE},
+ {AF_FORMAT_U32_BE, SND_PCM_FORMAT_U32_BE},
+ {AF_FORMAT_S32_LE, SND_PCM_FORMAT_S32_LE},
+ {AF_FORMAT_S32_BE, SND_PCM_FORMAT_S32_BE},
+ {AF_FORMAT_U24_LE, SND_PCM_FORMAT_U24_3LE},
+ {AF_FORMAT_U24_BE, SND_PCM_FORMAT_U24_3BE},
+ {AF_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE},
+ {AF_FORMAT_S24_BE, SND_PCM_FORMAT_S24_3BE},
+ {AF_FORMAT_FLOAT_LE, SND_PCM_FORMAT_FLOAT_LE},
+ {AF_FORMAT_FLOAT_BE, SND_PCM_FORMAT_FLOAT_BE},
+ {AF_FORMAT_AC3_LE, SND_PCM_FORMAT_S16_LE},
+ {AF_FORMAT_AC3_BE, SND_PCM_FORMAT_S16_BE},
+ {AF_FORMAT_IEC61937_LE, SND_PCM_FORMAT_S16_LE},
+ {AF_FORMAT_IEC61937_BE, SND_PCM_FORMAT_S16_BE},
+ {AF_FORMAT_MPEG2, SND_PCM_FORMAT_MPEG},
+ {AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN},
+};
+
+static int find_alsa_format(int af_format)
{
- int err, len;
- char *ac3_device, *args;
-
- if (try_ac3) {
- /* to set the non-audio bit, use AES0=6 */
- len = strlen(device);
- ac3_device = malloc(len + 7 + 1);
- if (!ac3_device)
- return -ENOMEM;
- strcpy(ac3_device, device);
- args = strchr(ac3_device, ':');
- if (!args) {
- /* no existing parameters: add it behind device name */
- strcat(ac3_device, ":AES0=6");
- } else {
- do
- ++args;
- while (isspace(*args));
- if (*args == '\0') {
- /* ":" but no parameters */
- strcat(ac3_device, "AES0=6");
- } else if (*args != '{') {
- /* a simple list of parameters: add it at the end of the list */
- strcat(ac3_device, ",AES0=6");
- } else {
- /* parameters in config syntax: add it inside the { } block */
- do
- --len;
- while (len > 0 && isspace(ac3_device[len]));
- if (ac3_device[len] == '}')
- strcpy(ac3_device + len, " AES0=6}");
- }
+ for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) {
+ if (mp_to_alsa_format[n][0] == af_format)
+ return mp_to_alsa_format[n][1];
+ }
+ return SND_PCM_FORMAT_UNKNOWN;
+}
+
+// Lists device names and their implied channel map.
+// The second item must be resolvable with mp_chmap_from_str().
+// Source: http://www.alsa-project.org/main/index.php/DeviceNames
+// (Speaker names are slightly different from mpv's.)
+static const char *device_channel_layouts[][2] = {
+ {"default", "fc"},
+ {"default", "fl-fr"},
+ {"rear", "bl-br"},
+ {"center_lfe", "fc-lfe"},
+ {"side", "sl-sr"},
+ {"surround40", "fl-fr-fc-bc"},
+ {"surround50", "fl-fr-bl-br-fc"},
+ {"surround41", "fl-fr-bl-br-lfe"},
+ {"surround51", "fl-fr-bl-br-fc-lfe"},
+ {"surround71", "fl-fr-bl-br-fc-lfe-sl-sr"},
+};
+
+#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
+
+#define NUM_ALSA_CHMAPS ARRAY_LEN(device_channel_layouts)
+
+static const char *select_chmap(struct ao *ao)
+{
+ struct mp_chmap_sel sel = {0};
+ struct mp_chmap maps[NUM_ALSA_CHMAPS];
+ for (int n = 0; n < NUM_ALSA_CHMAPS; n++) {
+ mp_chmap_from_str(&maps[n], bstr0(device_channel_layouts[n][1]));
+ mp_chmap_sel_add_map(&sel, &maps[n]);
+ };
+
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ return NULL;
+
+ for (int n = 0; n < NUM_ALSA_CHMAPS; n++) {
+ if (mp_chmap_equals(&ao->channels, &maps[n]))
+ return device_channel_layouts[n][0];
+ }
+
+ char *name = mp_chmap_to_str(&ao->channels);
+ mp_tmsg(MSGT_AO, MSGL_ERR,
+ "[AO_ALSA] channel layout %s (%d ch) not supported.\n",
+ name, ao->channels.num);
+ talloc_free(name);
+ return "default";
+}
+
+static int try_open_device(struct ao *ao, const char *device, int open_mode,
+ int try_ac3)
+{
+ struct priv *p = ao->priv;
+ int err, len;
+ char *ac3_device, *args;
+
+ if (try_ac3) {
+ /* to set the non-audio bit, use AES0=6 */
+ len = strlen(device);
+ ac3_device = malloc(len + 7 + 1);
+ if (!ac3_device)
+ return -ENOMEM;
+ strcpy(ac3_device, device);
+ args = strchr(ac3_device, ':');
+ if (!args) {
+ /* no existing parameters: add it behind device name */
+ strcat(ac3_device, ":AES0=6");
+ } else {
+ do {
+ ++args;
+ } while (isspace(*args));
+ if (*args == '\0') {
+ /* ":" but no parameters */
+ strcat(ac3_device, "AES0=6");
+ } else if (*args != '{') {
+ /* a simple list of parameters: add it at the end of the list */
+ strcat(ac3_device, ",AES0=6");
+ } else {
+ /* parameters in config syntax: add it inside the { } block */
+ do {
+ --len;
+ } while (len > 0 && isspace(ac3_device[len]));
+ if (ac3_device[len] == '}')
+ strcpy(ac3_device + len, " AES0=6}");
+ }
+ }
+ err = snd_pcm_open
+ (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, open_mode);
+ free(ac3_device);
+ if (!err)
+ return 0;
}
- err = snd_pcm_open(&alsa_handler, ac3_device, SND_PCM_STREAM_PLAYBACK,
- open_mode);
- free(ac3_device);
- if (!err)
- return 0;
- }
- return snd_pcm_open(&alsa_handler, device, SND_PCM_STREAM_PLAYBACK,
- open_mode);
+ return snd_pcm_open
+ (&p->alsa, device, SND_PCM_STREAM_PLAYBACK, open_mode);
}
/*
open & setup audio device
- return: 1=success 0=fail
-*/
-static int init(int rate_hz, int channels, int format, int flags)
+ return: 0=success -1=fail
+ */
+static int init(struct ao *ao, char *params)
{
int err;
int block;
@@ -340,89 +421,29 @@ static int init(int rate_hz, int channels, int format, int flags)
snd_pcm_uframes_t bufsize;
snd_pcm_uframes_t boundary;
const opt_t subopts[] = {
- {"block", OPT_ARG_BOOL, &block, NULL},
- {"device", OPT_ARG_STR, &device, str_maxlen},
- {NULL}
+ {"block", OPT_ARG_BOOL, &block, NULL},
+ {"device", OPT_ARG_STR, &device, str_maxlen},
+ {NULL}
};
+ struct priv *p = talloc_zero(ao, struct priv);
+ ao->priv = p;
+
char alsa_device[ALSA_DEVICE_SIZE + 1];
// make sure alsa_device is null-terminated even when using strncpy etc.
memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1);
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %x\n", rate_hz,
- channels, format);
- alsa_handler = NULL;
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: using ALSA %s\n", snd_asoundlib_version());
+ mp_msg(MSGT_AO, MSGL_V,
+ "alsa-init: requested format: %d Hz, %d channels, %x\n",
+ ao->samplerate, ao->channels.num, ao->format);
+ p->alsa = NULL;
+ mp_msg(MSGT_AO, MSGL_V, "alsa-init: using ALSA %s\n", snd_asoundlib_version());
- prepause_frames = 0;
- delay_before_pause = 0;
+ p->prepause_frames = 0;
+ p->delay_before_pause = 0;
snd_lib_error_set_handler(alsa_error_handler);
- ao_data.samplerate = rate_hz;
- ao_data.format = format;
- ao_data.channels = channels;
-
- switch (format)
- {
- case AF_FORMAT_S8:
- alsa_format = SND_PCM_FORMAT_S8;
- break;
- case AF_FORMAT_U8:
- alsa_format = SND_PCM_FORMAT_U8;
- break;
- case AF_FORMAT_U16_LE:
- alsa_format = SND_PCM_FORMAT_U16_LE;
- break;
- case AF_FORMAT_U16_BE:
- alsa_format = SND_PCM_FORMAT_U16_BE;
- break;
- case AF_FORMAT_AC3_LE:
- case AF_FORMAT_S16_LE:
- case AF_FORMAT_IEC61937_LE:
- alsa_format = SND_PCM_FORMAT_S16_LE;
- break;
- case AF_FORMAT_AC3_BE:
- case AF_FORMAT_S16_BE:
- case AF_FORMAT_IEC61937_BE:
- alsa_format = SND_PCM_FORMAT_S16_BE;
- break;
- case AF_FORMAT_U32_LE:
- alsa_format = SND_PCM_FORMAT_U32_LE;
- break;
- case AF_FORMAT_U32_BE:
- alsa_format = SND_PCM_FORMAT_U32_BE;
- break;
- case AF_FORMAT_S32_LE:
- alsa_format = SND_PCM_FORMAT_S32_LE;
- break;
- case AF_FORMAT_S32_BE:
- alsa_format = SND_PCM_FORMAT_S32_BE;
- break;
- case AF_FORMAT_U24_LE:
- alsa_format = SND_PCM_FORMAT_U24_3LE;
- break;
- case AF_FORMAT_U24_BE:
- alsa_format = SND_PCM_FORMAT_U24_3BE;
- break;
- case AF_FORMAT_S24_LE:
- alsa_format = SND_PCM_FORMAT_S24_3LE;
- break;
- case AF_FORMAT_S24_BE:
- alsa_format = SND_PCM_FORMAT_S24_3BE;
- break;
- case AF_FORMAT_FLOAT_LE:
- alsa_format = SND_PCM_FORMAT_FLOAT_LE;
- break;
- case AF_FORMAT_FLOAT_BE:
- alsa_format = SND_PCM_FORMAT_FLOAT_BE;
- break;
-
- default:
- alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1
- break;
- }
-
//subdevice parsing
// set defaults
block = 1;
@@ -432,347 +453,271 @@ static int init(int rate_hz, int channels, int format, int flags)
* while opening the abstract alias for the spdif subdevice
* 'iec958'
*/
- if (AF_FORMAT_IS_IEC61937(format)) {
- device.str = "iec958";
- mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels);
- }
- else
- /* in any case for multichannel playback we should select
- * appropriate device
- */
- switch (channels) {
- case 1:
- case 2:
- device.str = "default";
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n");
- break;
- case 4:
- if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
- // hack - use the converter plugin
- device.str = "plug:surround40";
- else
- device.str = "surround40";
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n");
- break;
- case 6:
- if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
- device.str = "plug:surround51";
- else
- device.str = "surround51";
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n");
- break;
- case 8:
- if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
- device.str = "plug:surround71";
- else
- device.str = "surround71";
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n");
- break;
- default:
- device.str = "default";
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] %d channels are not supported.\n",channels);
+ device.str = NULL;
+ if (AF_FORMAT_IS_IEC61937(ao->format)) {
+ device.str = "iec958";
+ mp_msg(MSGT_AO, MSGL_V,
+ "alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n",
+ ao->channels.num);
+ } else {
+ device.str = select_chmap(ao);
+ if (strcmp(device.str, "default") != 0 && ao->format == AF_FORMAT_FLOAT_NE)
+ {
+ // hack - use the converter plugin (why the heck?)
+ device.str = talloc_asprintf(ao, "plug:%s", device.str);
}
+ }
device.len = strlen(device.str);
- if (subopt_parse(ao_subdevice, subopts) != 0) {
+ if (subopt_parse(params, subopts) != 0) {
print_help();
return 0;
}
parse_device(alsa_device, device.str, device.len);
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: using device %s\n", alsa_device);
-
- alsa_can_pause = 1;
-
- if (!alsa_handler) {
- int open_mode = block ? 0 : SND_PCM_NONBLOCK;
- int isac3 = AF_FORMAT_IS_IEC61937(format);
- //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
- if ((err = try_open_device(alsa_device, open_mode, isac3)) < 0)
- {
- if (err != -EBUSY && !block) {
- mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Open in nonblock-mode failed, trying to open in block-mode.\n");
- if ((err = try_open_device(alsa_device, 0, isac3)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err));
- return 0;
- }
- } else {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err));
- return 0;
- }
- }
-
- if ((err = snd_pcm_nonblock(alsa_handler, 0)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AL_ALSA] Error setting block-mode %s.\n", snd_strerror(err));
- } else {
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: pcm opened in blocking mode\n");
- }
-
- snd_pcm_hw_params_t *alsa_hwparams;
- snd_pcm_sw_params_t *alsa_swparams;
-
- snd_pcm_hw_params_alloca(&alsa_hwparams);
- snd_pcm_sw_params_alloca(&alsa_swparams);
-
- // setting hw-parameters
- if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get initial parameters: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,
- SND_PCM_ACCESS_RW_INTERLEAVED);
- if (err < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set access type: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- /* workaround for nonsupported formats
- sets default format to S16_LE if the given formats aren't supported */
- if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams,
- alsa_format)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_INFO,
- "[AO_ALSA] Format %s is not supported by hardware, trying default.\n", af_fmt2str_short(format));
- alsa_format = SND_PCM_FORMAT_S16_LE;
- if (AF_FORMAT_IS_AC3(ao_data.format))
- ao_data.format = AF_FORMAT_AC3_LE;
- else if (AF_FORMAT_IS_IEC61937(ao_data.format))
- ao_data.format = AF_FORMAT_IEC61937_LE;
- else
- ao_data.format = AF_FORMAT_S16_LE;
- }
-
- if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
- alsa_format)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set format: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- if ((err = snd_pcm_hw_params_set_channels_near(alsa_handler, alsa_hwparams,
- &ao_data.channels)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set channels: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
- prefer our own resampler, since that allows users to choose the resampler,
- even per file if desired */
- if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams,
- 0)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to disable resampling: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams,
- &ao_data.samplerate, NULL)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set samplerate-2: %s\n",
- snd_strerror(err));
- return 0;
+ mp_msg(MSGT_AO, MSGL_V, "alsa-init: using device %s\n", alsa_device);
+
+ p->can_pause = 1;
+
+ int open_mode = block ? 0 : SND_PCM_NONBLOCK;
+ int isac3 = AF_FORMAT_IS_IEC61937(ao->format);
+ //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
+ err = try_open_device(ao, alsa_device, open_mode, isac3);
+ if (err < 0) {
+ if (err != -EBUSY && !block) {
+ mp_tmsg(MSGT_AO, MSGL_INFO, "[AO_ALSA] Open in nonblock-mode "
+ "failed, trying to open in block-mode.\n");
+ err = try_open_device(ao, alsa_device, 0, isac3);
}
+ CHECK_ALSA_ERROR("Playback open error");
+ }
- bytes_per_sample = af_fmt2bits(ao_data.format) / 8;
- bytes_per_sample *= ao_data.channels;
- ao_data.bps = ao_data.samplerate * bytes_per_sample;
-
- if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams,
- &(unsigned int){BUFFER_TIME}, NULL)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set buffer time near: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams,
- &(unsigned int){FRAGCOUNT}, NULL)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set periods: %s\n",
- snd_strerror(err));
- return 0;
- }
-
- /* finally install hardware parameters */
- if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set hw-parameters: %s\n",
- snd_strerror(err));
- return 0;
- }
- // end setting hw-params
-
-
- // gets buffersize for control
- if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get buffersize: %s\n", snd_strerror(err));
- return 0;
- }
- else {
- ao_data.buffersize = bufsize * bytes_per_sample;
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize);
- }
-
- if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO ALSA] Unable to get period size: %s\n", snd_strerror(err));
- return 0;
- } else {
- mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size);
- }
- ao_data.outburst = chunk_size * bytes_per_sample;
-
- /* setting software parameters */
- if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get sw-parameters: %s\n",
- snd_strerror(err));
- return 0;
- }
- if ((err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get boundary: %s\n",
- snd_strerror(err));
- return 0;
- }
- /* start playing when one period has been written */
- if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, chunk_size)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set start threshold: %s\n",
- snd_strerror(err));
- return 0;
- }
- /* disable underrun reporting */
- if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, boundary)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set stop threshold: %s\n",
- snd_strerror(err));
- return 0;
- }
- /* play silence when there is an underrun */
- if ((err = snd_pcm_sw_params_set_silence_size(alsa_handler, alsa_swparams, boundary)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to set silence size: %s\n",
- snd_strerror(err));
- return 0;
- }
- if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to get sw-parameters: %s\n",
- snd_strerror(err));
- return 0;
- }
- /* end setting sw-params */
-
- alsa_can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
-
- mp_msg(MSGT_AO,MSGL_V,"alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n",
- ao_data.samplerate, ao_data.channels, (int)bytes_per_sample, ao_data.buffersize,
- snd_pcm_format_description(alsa_format));
-
- } // end switch alsa_handler (spdif)
- return 1;
+ err = snd_pcm_nonblock(p->alsa, 0);
+ if (err < 0) {
+ mp_tmsg(MSGT_AO, MSGL_ERR,
+ "[AL_ALSA] Error setting block-mode %s.\n",
+ snd_strerror(err));
+ } else {
+ mp_msg(MSGT_AO, MSGL_V, "alsa-init: pcm opened in blocking mode\n");
+ }
+
+ snd_pcm_hw_params_t *alsa_hwparams;
+ snd_pcm_sw_params_t *alsa_swparams;
+
+ snd_pcm_hw_params_alloca(&alsa_hwparams);
+ snd_pcm_sw_params_alloca(&alsa_swparams);
+
+ // setting hw-parameters
+ err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
+ CHECK_ALSA_ERROR("Unable to get initial parameters");
+
+ err = snd_pcm_hw_params_set_access
+ (p->alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+ CHECK_ALSA_ERROR("Unable to set access type");
+
+ p->alsa_fmt = find_alsa_format(ao->format);
+ if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
+ p->alsa_fmt = SND_PCM_FORMAT_S16;
+ ao->format = AF_FORMAT_S16_NE;
+ }
+
+ err = snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt);
+ if (err < 0) {
+ mp_tmsg(MSGT_AO, MSGL_INFO, "[AO_ALSA] Format %s is not supported "
+ "by hardware, trying default.\n", af_fmt2str_short(ao->format));
+ p->alsa_fmt = SND_PCM_FORMAT_S16_LE;
+ if (AF_FORMAT_IS_AC3(ao->format))
+ ao->format = AF_FORMAT_AC3_LE;
+ else if (AF_FORMAT_IS_IEC61937(ao->format))
+ ao->format = AF_FORMAT_IEC61937_LE;
+ else
+ ao->format = AF_FORMAT_S16_LE;
+ }
+
+ err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
+ CHECK_ALSA_ERROR("Unable to set format");
+
+ int num_channels = ao->channels.num;
+ err = snd_pcm_hw_params_set_channels_near
+ (p->alsa, alsa_hwparams, &num_channels);
+ CHECK_ALSA_ERROR("Unable to set channels");
+
+ if (num_channels != ao->channels.num) {
+ mp_tmsg(MSGT_AO, MSGL_ERR,
+ "[AO_ALSA] Couldn't get requested number of channels.\n");
+ mp_chmap_from_channels_alsa(&ao->channels, num_channels);
+ }
+
+ /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
+ prefer our own resampler, since that allows users to choose the resampler,
+ even per file if desired */
+ err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
+ CHECK_ALSA_ERROR("Unable to disable resampling");
+
+ err = snd_pcm_hw_params_set_rate_near
+ (p->alsa, alsa_hwparams, &ao->samplerate, NULL);
+ CHECK_ALSA_ERROR("Unable to set samplerate-2");
+
+ p->bytes_per_sample = af_fmt2bits(ao->format) / 8;
+ p->bytes_per_sample *= ao->channels.num;
+ ao->bps = ao->samplerate * p->bytes_per_sample;
+
+ err = snd_pcm_hw_params_set_buffer_time_near
+ (p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL);
+ CHECK_ALSA_ERROR("Unable to set buffer time near");
+
+ err = snd_pcm_hw_params_set_periods_near
+ (p->alsa, alsa_hwparams, &(unsigned int){FRAGCOUNT}, NULL);
+ CHECK_ALSA_ERROR("Unable to set periods");
+
+ /* finally install hardware parameters */
+ err = snd_pcm_hw_params(p->alsa, alsa_hwparams);
+ CHECK_ALSA_ERROR("Unable to set hw-parameters");
+
+ // end setting hw-params
+
+ // gets buffersize for control
+ err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize);
+ CHECK_ALSA_ERROR("Unable to get buffersize");
+
+ ao->buffersize = bufsize * p->bytes_per_sample;
+ mp_msg(MSGT_AO, MSGL_V, "alsa-init: got buffersize=%i\n",
+ ao->buffersize);
+
+ err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL);
+ CHECK_ALSA_ERROR("Unable to get period size");
+
+ mp_msg(MSGT_AO, MSGL_V, "alsa-init: got period size %li\n", chunk_size);
+ ao->outburst = chunk_size * p->bytes_per_sample;
+
+ /* setting software parameters */
+ err = snd_pcm_sw_params_current(p->alsa, alsa_swparams);
+ CHECK_ALSA_ERROR("Unable to get sw-parameters");
+
+ err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary);
+ CHECK_ALSA_ERROR("Unable to get boundary");
+
+ /* start playing when one period has been written */
+ err = snd_pcm_sw_params_set_start_threshold
+ (p->alsa, alsa_swparams, chunk_size);
+ CHECK_ALSA_ERROR("Unable to set start threshold");
+
+ /* disable underrun reporting */
+ err = snd_pcm_sw_params_set_stop_threshold
+ (p->alsa, alsa_swparams, boundary);
+ CHECK_ALSA_ERROR("Unable to set stop threshold");
+
+ /* play silence when there is an underrun */
+ err = snd_pcm_sw_params_set_silence_size
+ (p->alsa, alsa_swparams, boundary);
+ CHECK_ALSA_ERROR("Unable to set silence size");
+
+ err = snd_pcm_sw_params(p->alsa, alsa_swparams);
+ CHECK_ALSA_ERROR("Unable to get sw-parameters");
+
+ /* end setting sw-params */
+
+ p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
+
+ mp_msg(MSGT_AO, MSGL_V,
+ "alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n",
+ ao->samplerate, ao->channels.num, (int)p->bytes_per_sample,
+ ao->buffersize, snd_pcm_format_description(p->alsa_fmt));
+
+ return 0;
+
+alsa_error:
+ return -1;
} // end init
/* close audio device */
-static void uninit(int immed)
+static void uninit(struct ao *ao, bool immed)
{
+ struct priv *p = ao->priv;
- if (alsa_handler) {
- int err;
+ if (p->alsa) {
+ int err;
+
+ if (!immed)
+ snd_pcm_drain(p->alsa);
- if (!immed)
- snd_pcm_drain(alsa_handler);
-
- if ((err = snd_pcm_close(alsa_handler)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm close error: %s\n", snd_strerror(err));
- return;
- }
- else {
- alsa_handler = NULL;
- mp_msg(MSGT_AO,MSGL_V,"alsa-uninit: pcm closed\n");
+ err = snd_pcm_close(p->alsa);
+ CHECK_ALSA_ERROR("pcm close error");
+
+ p->alsa = NULL;
+ mp_msg(MSGT_AO, MSGL_V, "alsa-uninit: pcm closed\n");
+ } else {
+ mp_tmsg(MSGT_AO, MSGL_ERR, "[AO_ALSA] No handler defined!\n");
}
- }
- else {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] No handler defined!\n");
- }
+
+alsa_error: ;
}
-static void audio_pause(void)
+static void audio_pause(struct ao *ao)
{
+ struct priv *p = ao->priv;
int err;
- if (alsa_can_pause) {
- delay_before_pause = get_delay();
- if ((err = snd_pcm_pause(alsa_handler, 1)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm pause error: %s\n", snd_strerror(err));
- return;
- }
- mp_msg(MSGT_AO,MSGL_V,"alsa-pause: pause supported by hardware\n");
+ if (p->can_pause) {
+ p->delay_before_pause = get_delay(ao);
+ err = snd_pcm_pause(p->alsa, 1);
+ CHECK_ALSA_ERROR("pcm pause error");
+ mp_msg(MSGT_AO, MSGL_V, "alsa-pause: pause supported by hardware\n");
} else {
- if (snd_pcm_delay(alsa_handler, &prepause_frames) < 0
- || prepause_frames < 0)
- prepause_frames = 0;
- delay_before_pause = prepause_frames / (float)ao_data.samplerate;
+ if (snd_pcm_delay(p->alsa, &p->prepause_frames) < 0
+ || p->prepause_frames < 0)
+ p->prepause_frames = 0;
+ p->delay_before_pause = p->prepause_frames / (float)ao->samplerate;
- if ((err = snd_pcm_drop(alsa_handler)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm drop error: %s\n", snd_strerror(err));
- return;
- }
+ err = snd_pcm_drop(p->alsa);
+ CHECK_ALSA_ERROR("pcm drop error");
}
+
+alsa_error: ;
}
-static void audio_resume(void)
+static void audio_resume(struct ao *ao)
{
+ struct priv *p = ao->priv;
int err;
- if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
- mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Pcm in suspend mode, trying to resume.\n");
- while ((err = snd_pcm_resume(alsa_handler)) == -EAGAIN) sleep(1);
+ if (snd_pcm_state(p->alsa) == SND_PCM_STATE_SUSPENDED) {
+ mp_tmsg(MSGT_AO, MSGL_INFO,
+ "[AO_ALSA] Pcm in suspend mode, trying to resume.\n");
+ while ((err = snd_pcm_resume(p->alsa)) == -EAGAIN)
+ sleep(1);
}
- if (alsa_can_pause) {
- if ((err = snd_pcm_pause(alsa_handler, 0)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm resume error: %s\n", snd_strerror(err));
- return;
- }
- mp_msg(MSGT_AO,MSGL_V,"alsa-resume: resume supported by hardware\n");
+ if (p->can_pause) {
+ err = snd_pcm_pause(p->alsa, 0);
+ CHECK_ALSA_ERROR("pcm resume error");
+ mp_msg(MSGT_AO, MSGL_V, "alsa-resume: resume supported by hardware\n");
} else {
- if ((err = snd_pcm_prepare(alsa_handler)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err));
- return;
- }
- if (prepause_frames) {
- void *silence = calloc(prepause_frames, bytes_per_sample);
- play(silence, prepause_frames * bytes_per_sample, 0);
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ if (p->prepause_frames) {
+ void *silence = calloc(p->prepause_frames, p->bytes_per_sample);
+ play(ao, silence, p->prepause_frames * p->bytes_per_sample, 0);
free(silence);
}
}
+
+alsa_error: ;
}
/* stop playing and empty buffers (for seeking/pause) */
-static void reset(void)
+static void reset(struct ao *ao)
{
+ struct priv *p = ao->priv;
int err;
- prepause_frames = 0;
- delay_before_pause = 0;
- if ((err = snd_pcm_drop(alsa_handler)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err));
- return;
- }
- if ((err = snd_pcm_prepare(alsa_handler)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(err));
- return;
- }
- return;
+ p->prepause_frames = 0;
+ p->delay_before_pause = 0;
+ err = snd_pcm_drop(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+
+alsa_error: ;
}
/*
@@ -780,91 +725,115 @@ static void reset(void)
returns: number of bytes played
modified last at 29.06.02 by jp
thanxs for marius <marius@rospot.com> for giving us the light ;)
-*/
+ */
-static int play(void* data, int len, int flags)
+static int play(struct ao *ao, void *data, int len, int flags)
{
- int num_frames;
- snd_pcm_sframes_t res = 0;
- if (!(flags & AOPLAY_FINAL_CHUNK))
- len = len / ao_data.outburst * ao_data.outburst;
- num_frames = len / bytes_per_sample;
+ struct priv *p = ao->priv;
+ int num_frames;
+ snd_pcm_sframes_t res = 0;
+ if (!(flags & AOPLAY_FINAL_CHUNK))
+ len = len / ao->outburst * ao->outburst;
+ num_frames = len / p->bytes_per_sample;
- //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len);
+ //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len);
- if (!alsa_handler) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Device configuration error.");
- return 0;
- }
+ if (!p->alsa) {
+ mp_tmsg(MSGT_AO, MSGL_ERR, "[AO_ALSA] Device configuration error.");
+ return 0;
+ }
- if (num_frames == 0)
- return 0;
+ if (num_frames == 0)
+ return 0;
+
+ do {
+ res = snd_pcm_writei(p->alsa, data, num_frames);
+
+ if (res == -EINTR) {
+ /* nothing to do */
+ res = 0;
+ } else if (res == -ESTRPIPE) { /* suspend */
+ mp_tmsg(MSGT_AO, MSGL_INFO,
+ "[AO_ALSA] Pcm in suspend mode, trying to resume.\n");
+ while ((res = snd_pcm_resume(p->alsa)) == -EAGAIN)
+ sleep(1);
+ }
+ if (res < 0) {
+ mp_tmsg(MSGT_AO, MSGL_ERR, "[AO_ALSA] Write error: %s\n",
+ snd_strerror(res));
+ mp_tmsg(MSGT_AO, MSGL_INFO,
+ "[AO_ALSA] Trying to reset soundcard.\n");
+ res = snd_pcm_prepare(p->alsa);
+ int err = res;
+ CHECK_ALSA_ERROR("pcm prepare error");
+ res = 0;
+ }
+ } while (res == 0);
+
+ return res < 0 ? 0 : res * p->bytes_per_sample;
- do {
- res = snd_pcm_writei(alsa_handler, data, num_frames);
-
- if (res == -EINTR) {
- /* nothing to do */
- res = 0;
- }
- else if (res == -ESTRPIPE) { /* suspend */
- mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Pcm in suspend mode, trying to resume.\n");
- while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
- sleep(1);
- }
- if (res < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Write error: %s\n", snd_strerror(res));
- mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Trying to reset soundcard.\n");
- if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] pcm prepare error: %s\n", snd_strerror(res));
- break;
- }
- res = 0;
- }
- } while (res == 0);
-
- return res < 0 ? 0 : res * bytes_per_sample;
+alsa_error:
+ return 0;
}
/* how many byes are free in the buffer */
-static int get_space(void)
+static int get_space(struct ao *ao)
{
+ struct priv *p = ao->priv;
snd_pcm_status_t *status;
- int ret;
+ int err;
snd_pcm_status_alloca(&status);
- if ((ret = snd_pcm_status(alsa_handler, status)) < 0)
- {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Cannot get pcm status: %s\n", snd_strerror(ret));
- return 0;
- }
+ err = snd_pcm_status(p->alsa, status);
+ CHECK_ALSA_ERROR("cannot get pcm status");
- unsigned space = snd_pcm_status_get_avail(status) * bytes_per_sample;
- if (space > ao_data.buffersize) // Buffer underrun?
- space = ao_data.buffersize;
+ unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample;
+ if (space > ao->buffersize) // Buffer underrun?
+ space = ao->buffersize;
return space;
+
+alsa_error:
+ return 0;
}
/* delay in seconds between first and last sample in buffer */
-static float get_delay(void)
+static float get_delay(struct ao *ao)
{
- if (alsa_handler) {
- snd_pcm_sframes_t delay;
+ struct priv *p = ao->priv;
+ if (p->alsa) {
+ snd_pcm_sframes_t delay;
- if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_PAUSED)
- return delay_before_pause;
+ if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED)
+ return p->delay_before_pause;
- if (snd_pcm_delay(alsa_handler, &delay) < 0)
- return 0;
+ if (snd_pcm_delay(p->alsa, &delay) < 0)
+ return 0;
- if (delay < 0) {
- /* underrun - move the application pointer forward to catch up */
- snd_pcm_forward(alsa_handler, -delay);
- delay = 0;
- }
- return (float)delay / (float)ao_data.samplerate;
- } else {
- return 0;
- }
+ if (delay < 0) {
+ /* underrun - move the application pointer forward to catch up */
+ snd_pcm_forward(p->alsa, -delay);
+ delay = 0;
+ }
+ return (float)delay / (float)ao->samplerate;
+ } else
+ return 0;
}
+
+const struct ao_driver audio_out_alsa = {
+ .info = &(const struct ao_info) {
+ "ALSA-0.9.x-1.x audio output",
+ "alsa",
+ "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>",
+ "under development"
+ },
+ .init = init,
+ .uninit = uninit,
+ .control = control,
+ .get_space = get_space,
+ .play = play,
+ .get_delay = get_delay,
+ .pause = audio_pause,
+ .resume = audio_resume,
+ .reset = reset,
+};
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index ba8f517c75..54ff1b4915 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -412,7 +412,7 @@ static void print_help(void)
free(devids);
}
-static int init(int rate,int channels,int format,int flags)
+static int init(int rate,const struct mp_chmap *channels,int format,int flags)
{
AudioStreamBasicDescription inDesc;
AudioComponentDescription desc;
@@ -439,7 +439,7 @@ int device_id, display_help = 0;
return 0;
}
- ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, channels, af_fmt2str_short(format), flags);
+ ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, ao_data.channels.num, af_fmt2str_short(format), flags);
ao = calloc(1, sizeof(ao_coreaudio_t));
@@ -499,10 +499,15 @@ int device_id, display_help = 0;
// Save selected device id
ao->i_selected_dev = devid_def;
+ struct mp_chmap_sel chmap_sel = {0};
+ mp_chmap_sel_add_waveext(&chmap_sel);
+ if (!ao_chmap_sel_adjust(&ao_data, &ao_data.channels, &chmap_sel))
+ goto err_out;
+
// Build Description for the input format
inDesc.mSampleRate=rate;
inDesc.mFormatID=ao->b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM;
- inDesc.mChannelsPerFrame=channels;
+ inDesc.mChannelsPerFrame=ao_data.channels.num;
inDesc.mBitsPerChannel=af_fmt2bits(format);
if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) {
@@ -521,7 +526,7 @@ int device_id, display_help = 0;
inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
inDesc.mFramesPerPacket = 1;
- ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8);
+ ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*ao_data.channels.num*(inDesc.mBitsPerChannel/8);
print_format(MSGL_V, "source:",&inDesc);
if (ao->b_supports_digital)
@@ -605,7 +610,8 @@ int device_id, display_help = 0;
ao->chunk_size = maxFrames;//*inDesc.mBytesPerFrame;
ao_data.samplerate = inDesc.mSampleRate;
- ao_data.channels = inDesc.mChannelsPerFrame;
+ if (!ao_chmap_sel_get_def(&ao_data, &chmap_sel, &ao_data.channels, inDesc.mChannelsPerFrame))
+ goto err_out2;
ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame;
ao_data.outburst = ao->chunk_size;
ao_data.buffersize = ao_data.bps;
@@ -837,7 +843,8 @@ static int OpenSPDIF(void)
ao->chunk_size = ao->stream_format.mBytesPerPacket;
ao_data.samplerate = ao->stream_format.mSampleRate;
- ao_data.channels = ao->stream_format.mChannelsPerFrame;
+ // Applies default ordering; ok because AC3 data is always in mpv internal channel order
+ mp_chmap_from_channels(&ao_data.channels, ao->stream_format.mChannelsPerFrame);
ao_data.bps = ao_data.samplerate * (ao->stream_format.mBytesPerPacket/ao->stream_format.mFramesPerPacket);
ao_data.outburst = ao->chunk_size;
ao_data.buffersize = ao_data.bps;
diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c
index 9ad27306bd..967084d3ae 100644
--- a/audio/out/ao_dsound.c
+++ b/audio/out/ao_dsound.c
@@ -64,26 +64,6 @@ LIBAO_EXTERN(dsound)
static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
-#define SPEAKER_FRONT_LEFT 0x1
-#define SPEAKER_FRONT_RIGHT 0x2
-#define SPEAKER_FRONT_CENTER 0x4
-#define SPEAKER_LOW_FREQUENCY 0x8
-#define SPEAKER_BACK_LEFT 0x10
-#define SPEAKER_BACK_RIGHT 0x20
-#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
-#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
-#define SPEAKER_BACK_CENTER 0x100
-#define SPEAKER_SIDE_LEFT 0x200
-#define SPEAKER_SIDE_RIGHT 0x400
-#define SPEAKER_TOP_CENTER 0x800
-#define SPEAKER_TOP_FRONT_LEFT 0x1000
-#define SPEAKER_TOP_FRONT_CENTER 0x2000
-#define SPEAKER_TOP_FRONT_RIGHT 0x4000
-#define SPEAKER_TOP_BACK_LEFT 0x8000
-#define SPEAKER_TOP_BACK_CENTER 0x10000
-#define SPEAKER_TOP_BACK_RIGHT 0x20000
-#define SPEAKER_RESERVED 0x80000000
-
#if 0
#define DSSPEAKER_HEADPHONE 0x00000001
#define DSSPEAKER_MONO 0x00000002
@@ -107,28 +87,6 @@ typedef struct {
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
#endif
-static const int channel_mask[] = {
- /* 1 */ SPEAKER_FRONT_CENTER,
- /* 2 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
- /* 3 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
- | SPEAKER_LOW_FREQUENCY,
- /* 4 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
- | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
- /* 5 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
- | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
- | SPEAKER_LOW_FREQUENCY,
- /* 6 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT
- | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
- | SPEAKER_LOW_FREQUENCY,
- /* 7 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT
- | SPEAKER_BACK_LEFT | SPEAKER_BACK_CENTER | SPEAKER_BACK_RIGHT
- | SPEAKER_LOW_FREQUENCY,
- /* 8 */ SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT
- | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
- | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
- | SPEAKER_LOW_FREQUENCY,
-};
-
static HINSTANCE hdsound_dll = NULL; ///handle to the dll
static LPDIRECTSOUND hds = NULL; ///direct sound object
static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer
@@ -349,22 +307,9 @@ static int write_buffer(unsigned char *data, int len)
if (SUCCEEDED(res))
{
if (!AF_FORMAT_IS_AC3(ao_data.format)) {
- int sampsize = af_fmt2bits(ao_data.format) / 8;
- reorder_channel_copy_nch(data,
- AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
- lpvPtr1,
- AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
- ao_data.channels,
- dwBytes1 / sampsize,
- sampsize);
+ memcpy(lpvPtr1, data, dwBytes1);
if (lpvPtr2 != NULL)
- reorder_channel_copy_nch(data + dwBytes1,
- AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
- lpvPtr2,
- AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
- ao_data.channels,
- dwBytes2 / sampsize,
- sampsize);
+ memcpy(lpvPtr2, (char *)data + dwBytes1, dwBytes2);
write_offset+=dwBytes1+dwBytes2;
if(write_offset>=buffer_size)
@@ -432,7 +377,7 @@ static int control(int cmd, void *arg)
\param flags unused
\return 1=success 0=fail
*/
-static int init(int rate, int channels, int format, int flags)
+static int init(int rate, const struct mp_chmap *channels, int format, int flags)
{
int res;
if (!InitDirectSound()) return 0;
@@ -445,15 +390,14 @@ static int init(int rate, int channels, int format, int flags)
DSBUFFERDESC dsbpridesc;
DSBUFFERDESC dsbdesc;
- //check if the channel count and format is supported in general
- if (channels > FF_ARRAY_ELEMS(channel_mask)) {
- UninitDirectSound();
- mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
- return 0;
- }
-
- if (AF_FORMAT_IS_AC3(format))
+ if (AF_FORMAT_IS_AC3(format)) {
format = AF_FORMAT_AC3_NE;
+ } else {
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext(&sel);
+ if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
+ return 0;
+ }
switch(format){
case AF_FORMAT_AC3_NE:
case AF_FORMAT_S24_LE:
@@ -465,25 +409,24 @@ static int init(int rate, int channels, int format, int flags)
format=AF_FORMAT_S16_LE;
}
//fill global ao_data
- ao_data.channels = channels;
ao_data.samplerate = rate;
ao_data.format = format;
- ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
+ ao_data.bps = ao_data.channels.num * rate * (af_fmt2bits(format)>>3);
if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
- mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
+ mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, ao_data.channels.num, af_fmt2str_short(format));
mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000);
//fill waveformatex
ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
- wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
- wformat.Format.nChannels = channels;
+ wformat.Format.cbSize = (ao_data.channels.num > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
+ wformat.Format.nChannels = ao_data.channels.num;
wformat.Format.nSamplesPerSec = rate;
if (AF_FORMAT_IS_AC3(format)) {
wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
wformat.Format.wBitsPerSample = 16;
wformat.Format.nBlockAlign = 4;
} else {
- wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
+ wformat.Format.wFormatTag = (ao_data.channels.num > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
wformat.Format.wBitsPerSample = af_fmt2bits(format);
wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
}
@@ -503,8 +446,8 @@ static int init(int rate, int channels, int format, int flags)
| DSBCAPS_GLOBALFOCUS /** Allows background playing */
| DSBCAPS_CTRLVOLUME; /** volume control enabled */
- if (channels > 2) {
- wformat.dwChannelMask = channel_mask[channels - 1];
+ if (ao_data.channels.num > 2) {
+ wformat.dwChannelMask = mp_chmap_to_waveext(&ao_data.channels);
wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
// Needed for 5.1 on emu101k - shit soundblaster
diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c
index b75122dc17..0f8baeab86 100644
--- a/audio/out/ao_jack.c
+++ b/audio/out/ao_jack.c
@@ -50,7 +50,7 @@ static const ao_info_t info =
LIBAO_EXTERN(jack)
//! maximum number of channels supported, avoids lots of mallocs
-#define MAX_CHANS 8
+#define MAX_CHANS MP_NUM_CHANNELS
static jack_port_t *ports[MAX_CHANS];
static int num_ports; ///< Number of used ports == number of channels
static jack_client_t *client;
@@ -202,7 +202,8 @@ static void print_help (void)
);
}
-static int init(int rate, int channels, int format, int flags) {
+static int init(int rate, const struct mp_chmap *channels, int format, int flags)
+{
const char **matching_ports = NULL;
char *port_name = NULL;
char *client_name = NULL;
@@ -222,10 +223,12 @@ static int init(int rate, int channels, int format, int flags) {
print_help();
return 0;
}
- if (channels > MAX_CHANS) {
- mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] Invalid number of channels: %i\n", channels);
+
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext(&sel);
+ if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
goto err_out;
- }
+
if (!client_name) {
client_name = malloc(40);
sprintf(client_name, "mpv [%d]", getpid());
@@ -249,9 +252,9 @@ static int init(int rate, int channels, int format, int flags) {
goto err_out;
}
i = 1;
+ num_ports = ao_data.channels.num;
while (matching_ports[i]) i++;
- if (channels > i) channels = i;
- num_ports = channels;
+ if (num_ports > i) num_ports = i;
// create out output ports
for (i = 0; i < num_ports; i++) {
@@ -281,10 +284,12 @@ static int init(int rate, int channels, int format, int flags) {
/ (float)rate;
callback_interval = 0;
- ao_data.channels = channels;
+ if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, num_ports))
+ goto err_out;
+
ao_data.samplerate = rate;
ao_data.format = AF_FORMAT_FLOAT_NE;
- ao_data.bps = channels * rate * sizeof(float);
+ ao_data.bps = ao_data.channels.num * rate * sizeof(float);
ao_data.buffersize = CHUNK_SIZE * NUM_CHUNKS;
ao_data.outburst = CHUNK_SIZE;
free(matching_ports);
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 33139ce866..d8185a8dca 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -101,7 +101,14 @@ static int init(struct ao *ao, char *params)
ac->stream->codec->time_base.den = ao->samplerate;
ac->stream->codec->sample_rate = ao->samplerate;
- ac->stream->codec->channels = ao->channels;
+
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_any(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ return -1;
+ mp_chmap_reorder_to_lavc(&ao->channels);
+ ac->stream->codec->channels = ao->channels.num;
+ ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels);
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
@@ -243,36 +250,6 @@ out_takefirst:
ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
- switch (ao->channels) {
- case 1:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_MONO;
- break;
- case 2:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_STEREO;
- break;
- /* someone please check if these are what mplayer normally assumes
- case 3:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_SURROUND;
- break;
- case 4:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_2_2;
- break;
- */
- case 5:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT0;
- break;
- case 6:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_5POINT1;
- break;
- case 8:
- ac->stream->codec->channel_layout = AV_CH_LAYOUT_7POINT1;
- break;
- default:
- mp_msg(MSGT_ENCODE, MSGL_ERR,
- "ao-lavc: unknown channel layout; hoping for the best\n");
- break;
- }
-
if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
return -1;
@@ -282,11 +259,12 @@ out_takefirst:
if (ac->pcmhack) {
ac->aframesize = 16384; // "enough"
- ac->buffer_size = ac->aframesize * ac->pcmhack * ao->channels * 2 + 200;
+ ac->buffer_size =
+ ac->aframesize * ac->pcmhack * ao->channels.num * 2 + 200;
} else {
ac->aframesize = ac->stream->codec->frame_size;
- ac->buffer_size = ac->aframesize * ac->sample_size * ao->channels * 2 +
- 200;
+ ac->buffer_size =
+ ac->aframesize * ac->sample_size * ao->channels.num * 2 + 200;
}
if (ac->buffer_size < FF_MIN_BUFFER_SIZE)
ac->buffer_size = FF_MIN_BUFFER_SIZE;
@@ -304,10 +282,10 @@ out_takefirst:
ac->offset_left = ac->offset;
//fill_ao_data:
- ao->outburst = ac->aframesize * ac->sample_size * ao->channels *
- ac->framecount;
+ ao->outburst =
+ ac->aframesize * ac->sample_size * ao->channels.num * ac->framecount;
ao->buffersize = ao->outburst * 2;
- ao->bps = ao->channels * ao->samplerate * ac->sample_size;
+ ao->bps = ao->channels.num * ao->samplerate * ac->sample_size;
ao->untimed = true;
ao->priv = ac;
@@ -348,7 +326,7 @@ static void uninit(struct ao *ao, bool cut_audio)
// TRICK: append aframesize-1 samples to the end, then play() will
// encode all it can
size_t extralen =
- (ac->aframesize - 1) * ao->channels * ac->sample_size;
+ (ac->aframesize - 1) * ao->channels.num * ac->sample_size;
void *paddingbuf = talloc_size(ao, ao->buffer.len + extralen);
memcpy(paddingbuf, ao->buffer.start, ao->buffer.len);
fill_with_padding((char *) paddingbuf + ao->buffer.len,
@@ -392,12 +370,6 @@ static int encode(struct ao *ao, double apts, void *data)
int status, gotpacket;
ac->aframecount++;
- if (data && (ao->channels == 5 || ao->channels == 6 || ao->channels == 8)) {
- reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
- AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
- ao->channels,
- ac->aframesize * ao->channels, ac->sample_size);
- }
if (data)
ectx->audio_pts_offset = realapts - apts;
@@ -411,12 +383,18 @@ static int encode(struct ao *ao, double apts, void *data)
frame->nb_samples = ac->aframesize;
if (ac->planarize) {
- void *data2 = talloc_size(ao, ac->aframesize * ao->channels * ac->sample_size);
- reorder_to_planar(data2, data, ac->sample_size, ao->channels, ac->aframesize);
+ void *data2 = talloc_size(ao, ac->aframesize * ao->channels.num *
+ ac->sample_size);
+ reorder_to_planar(data2, data, ac->sample_size, ao->channels.num,
+ ac->aframesize);
data = data2;
}
- if (avcodec_fill_audio_frame(frame, ao->channels, ac->stream->codec->sample_fmt, data, ac->aframesize * ao->channels * ac->sample_size, 1)) {
+ size_t audiolen = ac->aframesize * ao->channels.num * ac->sample_size;
+ if (avcodec_fill_audio_frame(frame, ao->channels.num,
+ ac->stream->codec->sample_fmt, data,
+ audiolen, 1))
+ {
mp_msg(MSGT_ENCODE, MSGL_ERR, "ao-lavc: error filling\n");
return -1;
}
@@ -523,7 +501,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
double pts = ao->pts;
double outpts;
- len /= ac->sample_size * ao->channels;
+ len /= ac->sample_size * ao->channels.num;
if (!encode_lavc_start(ectx)) {
mp_msg(MSGT_ENCODE, MSGL_WARN,
@@ -593,7 +571,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
if (ac->offset_left <= -len) {
// skip whole frame
ac->offset_left += len;
- return len * ac->sample_size * ao->channels;
+ return len * ac->sample_size * ao->channels.num;
} else {
// skip part of this frame, buffer/encode the rest
bufpos -= ac->offset_left;
@@ -604,11 +582,11 @@ static int play(struct ao *ao, void *data, int len, int flags)
// make a temporary buffer, filled with zeroes at the start
// (don't worry, only happens once)
- paddingbuf = talloc_size(ac, ac->sample_size * ao->channels *
+ paddingbuf = talloc_size(ac, ac->sample_size * ao->channels.num *
(ac->offset_left + len));
fill_with_padding(paddingbuf, ac->offset_left, ac->sample_size,
ac->sample_padding);
- data = (char *) paddingbuf + ac->sample_size * ao->channels *
+ data = (char *) paddingbuf + ac->sample_size * ao->channels.num *
ac->offset_left;
bufpos -= ac->offset_left; // yes, negative!
ptsoffset += ac->offset_left;
@@ -651,7 +629,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
while (len - bufpos >= ac->aframesize) {
encode(ao,
outpts + (bufpos + ptsoffset) / (double) ao->samplerate + encode_lavc_getoffset(ectx, ac->stream),
- (char *) data + ac->sample_size * bufpos * ao->channels);
+ (char *) data + ac->sample_size * bufpos * ao->channels.num);
bufpos += ac->aframesize;
}
@@ -667,11 +645,10 @@ static int play(struct ao *ao, void *data, int len, int flags)
ectx->next_in_pts = nextpts;
}
- return bufpos * ac->sample_size * ao->channels;
+ return bufpos * ac->sample_size * ao->channels.num;
}
const struct ao_driver audio_out_lavc = {
- .is_new = true,
.encode = true,
.info = &(const struct ao_info) {
"audio encoding using libavcodec",
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index 102f0a7013..53ec2a9a83 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -48,11 +48,17 @@ static int init(struct ao *ao, char *params)
{
struct priv *priv = talloc_zero(ao, struct priv);
ao->priv = priv;
+
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_any(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ return -1;
+
int samplesize = af_fmt2bits(ao->format) / 8;
- ao->outburst = 256 * ao->channels * samplesize;
+ ao->outburst = 256 * ao->channels.num * samplesize;
// A "buffer" for about 0.2 seconds of audio
ao->buffersize = (int)(ao->samplerate * 0.2 / 256 + 1) * ao->outburst;
- ao->bps = ao->channels * ao->samplerate * samplesize;
+ ao->bps = ao->channels.num * ao->samplerate * samplesize;
priv->last_time = GetTimer();
return 0;
@@ -110,7 +116,6 @@ static float get_delay(struct ao *ao)
}
const struct ao_driver audio_out_null = {
- .is_new = true,
.info = &(const struct ao_info) {
"Null audio output",
"null",
diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c
index 157cf93ac4..08f8bc1978 100644
--- a/audio/out/ao_openal.c
+++ b/audio/out/ao_openal.c
@@ -53,7 +53,7 @@ static const ao_info_t info =
LIBAO_EXTERN(openal)
-#define MAX_CHANS 8
+#define MAX_CHANS MP_NUM_CHANNELS
#define NUM_BUF 128
#define CHUNK_SIZE 512
static ALuint buffers[MAX_CHANS][NUM_BUF];
@@ -109,15 +109,29 @@ static void list_devices(void) {
}
}
-static int init(int rate, int channels, int format, int flags) {
+struct speaker {
+ int id;
+ float pos[3];
+};
+
+static const struct speaker speaker_pos[] = {
+ {MP_SPEAKER_ID_FL, {-1, 0, 0.5}},
+ {MP_SPEAKER_ID_FR, { 1, 0, 0.5}},
+ {MP_SPEAKER_ID_FC, { 0, 0, 1}},
+ {MP_SPEAKER_ID_LFE, { 0, 0, 0.1}},
+ {MP_SPEAKER_ID_BL, {-1, 0, -1}},
+ {MP_SPEAKER_ID_BR, { 1, 0, -1}},
+ {MP_SPEAKER_ID_BC, { 0, 0, -1}},
+ {MP_SPEAKER_ID_SL, {-1, 0, 0}},
+ {MP_SPEAKER_ID_SR, { 1, 0, 0}},
+ {-1},
+};
+
+static int init(int rate, const struct mp_chmap *channels, int format,
+ int flags)
+{
float position[3] = {0, 0, 0};
float direction[6] = {0, 0, 1, 0, -1, 0};
- float sppos[MAX_CHANS][3] = {
- {-1, 0, 0.5}, {1, 0, 0.5},
- {-1, 0, -1}, {1, 0, -1},
- {0, 0, 1}, {0, 0, 0.1},
- {-1, 0, 0}, {1, 0, 0},
- };
ALCdevice *dev = NULL;
ALCcontext *ctx = NULL;
ALCint freq = 0;
@@ -137,9 +151,22 @@ static int init(int rate, int channels, int format, int flags) {
list_devices();
goto err_out;
}
- if (channels > MAX_CHANS) {
- mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Invalid number of channels: %i\n", channels);
+ struct mp_chmap_sel sel = {0};
+ for (i = 0; speaker_pos[i].id != -1; i++)
+ mp_chmap_sel_add_speaker(&sel, speaker_pos[i].id);
+ if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
goto err_out;
+ struct speaker speakers[MAX_CHANS];
+ for (i = 0; i < ao_data.channels.num; i++) {
+ speakers[i].id = -1;
+ for (int n = 0; speaker_pos[n].id >= 0; n++) {
+ if (speaker_pos[n].id == ao_data.channels.speaker[i])
+ speakers[i] = speaker_pos[n];
+ }
+ if (speakers[i].id < 0) {
+ mp_msg(MSGT_AO, MSGL_FATAL, "[OpenAL] Unknown channel layout\n");
+ goto err_out;
+ }
}
dev = alcOpenDevice(device);
if (!dev) {
@@ -150,25 +177,22 @@ static int init(int rate, int channels, int format, int flags) {
alcMakeContextCurrent(ctx);
alListenerfv(AL_POSITION, position);
alListenerfv(AL_ORIENTATION, direction);
- alGenSources(channels, sources);
- for (i = 0; i < channels; i++) {
+ alGenSources(ao_data.channels.num, sources);
+ for (i = 0; i < ao_data.channels.num; i++) {
cur_buf[i] = 0;
unqueue_buf[i] = 0;
alGenBuffers(NUM_BUF, buffers[i]);
- alSourcefv(sources[i], AL_POSITION, sppos[i]);
+ alSourcefv(sources[i], AL_POSITION, speakers[i].pos);
alSource3f(sources[i], AL_VELOCITY, 0, 0, 0);
}
- if (channels == 1)
- alSource3f(sources[0], AL_POSITION, 0, 0, 1);
- ao_data.channels = channels;
alcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
if (alcGetError(dev) == ALC_NO_ERROR && freq)
rate = freq;
ao_data.samplerate = rate;
ao_data.format = AF_FORMAT_S16_NE;
- ao_data.bps = channels * rate * 2;
+ ao_data.bps = ao_data.channels.num * rate * 2;
ao_data.buffersize = CHUNK_SIZE * NUM_BUF;
- ao_data.outburst = channels * CHUNK_SIZE;
+ ao_data.outburst = ao_data.channels.num * CHUNK_SIZE;
tmpbuf = malloc(CHUNK_SIZE);
free(device);
return 1;
@@ -200,7 +224,7 @@ static void uninit(int immed) {
static void unqueue_buffers(void) {
ALint p;
int s;
- for (s = 0; s < ao_data.channels; s++) {
+ for (s = 0; s < ao_data.channels.num; s++) {
int till_wrap = NUM_BUF - unqueue_buf[s];
alGetSourcei(sources[s], AL_BUFFERS_PROCESSED, &p);
if (p >= till_wrap) {
@@ -219,7 +243,7 @@ static void unqueue_buffers(void) {
* \brief stop playing and empty buffers (for seeking/pause)
*/
static void reset(void) {
- alSourceStopv(ao_data.channels, sources);
+ alSourceStopv(ao_data.channels.num, sources);
unqueue_buffers();
}
@@ -227,14 +251,14 @@ static void reset(void) {
* \brief stop playing, keep buffers (for pause)
*/
static void audio_pause(void) {
- alSourcePausev(ao_data.channels, sources);
+ alSourcePausev(ao_data.channels.num, sources);
}
/**
* \brief resume playing, after audio_pause()
*/
static void audio_resume(void) {
- alSourcePlayv(ao_data.channels, sources);
+ alSourcePlayv(ao_data.channels.num, sources);
}
static int get_space(void) {
@@ -243,7 +267,7 @@ static int get_space(void) {
alGetSourcei(sources[0], AL_BUFFERS_QUEUED, &queued);
queued = NUM_BUF - queued - 3;
if (queued < 0) return 0;
- return queued * CHUNK_SIZE * ao_data.channels;
+ return queued * CHUNK_SIZE * ao_data.channels.num;
}
/**
@@ -254,22 +278,22 @@ static int play(void *data, int len, int flags) {
int i, j, k;
int ch;
int16_t *d = data;
- len /= ao_data.channels * CHUNK_SIZE;
+ len /= ao_data.channels.num * CHUNK_SIZE;
for (i = 0; i < len; i++) {
- for (ch = 0; ch < ao_data.channels; ch++) {
- for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels)
+ for (ch = 0; ch < ao_data.channels.num; ch++) {
+ for (j = 0, k = ch; j < CHUNK_SIZE / 2; j++, k += ao_data.channels.num)
tmpbuf[j] = d[k];
alBufferData(buffers[ch][cur_buf[ch]], AL_FORMAT_MONO16, tmpbuf,
CHUNK_SIZE, ao_data.samplerate);
alSourceQueueBuffers(sources[ch], 1, &buffers[ch][cur_buf[ch]]);
cur_buf[ch] = (cur_buf[ch] + 1) % NUM_BUF;
}
- d += ao_data.channels * CHUNK_SIZE / 2;
+ d += ao_data.channels.num * CHUNK_SIZE / 2;
}
alGetSourcei(sources[0], AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) // checked here in case of an underrun
- alSourcePlayv(ao_data.channels, sources);
- return len * ao_data.channels * CHUNK_SIZE;
+ alSourcePlayv(ao_data.channels.num, sources);
+ return len * ao_data.channels.num * CHUNK_SIZE;
}
static float get_delay(void) {
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
index fa8eccdeea..4ff97b30b6 100644
--- a/audio/out/ao_oss.c
+++ b/audio/out/ao_oss.c
@@ -221,12 +221,12 @@ static int control(int cmd,void *arg){
// open & setup audio device
// return: 1=success 0=fail
-static int init(int rate,int channels,int format,int flags){
+static int init(int rate,const struct mp_chmap *channels,int format,int flags){
char *mixer_channels [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
int oss_format;
char *mdev = mixer_device, *mchan = mixer_channel;
- mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,channels,
+ mp_msg(MSGT_AO,MSGL_V,"ao2: %d Hz %d chans %s\n",rate,ao_data.channels.num,
af_fmt2str_short(format));
if (ao_subdevice) {
@@ -339,25 +339,31 @@ ac3_retry:
mp_msg(MSGT_AO,MSGL_V,"audio_setup: sample format: %s (requested: %s)\n",
af_fmt2str_short(ao_data.format), af_fmt2str_short(format));
- ao_data.channels = channels;
if(!AF_FORMAT_IS_AC3(format)) {
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_alsa_def(&sel);
+ if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
+ return 0;
+ int reqchannels = ao_data.channels.num;
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
- if (ao_data.channels > 2) {
- if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels) == -1 ||
- ao_data.channels != channels ) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", channels);
+ if (reqchannels > 2) {
+ int nchannels = reqchannels;
+ if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 ||
+ nchannels != reqchannels ) {
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", reqchannels);
return 0;
}
}
else {
- int c = ao_data.channels-1;
+ int c = reqchannels-1;
if (ioctl (audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
- mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", ao_data.channels);
+ mp_tmsg(MSGT_AO,MSGL_ERR,"[AO OSS] audio_setup: Failed to set audio device to %d channels.\n", reqchannels);
return 0;
}
- ao_data.channels=c+1;
+ if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, c + 1))
+ return 0;
}
- mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels, channels);
+ mp_msg(MSGT_AO,MSGL_V,"audio_setup: using %d channels (requested: %d)\n", ao_data.channels.num, reqchannels);
// set rate
ao_data.samplerate=rate;
ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
@@ -403,7 +409,7 @@ ac3_retry:
#endif
}
- ao_data.bps=ao_data.channels;
+ ao_data.bps=ao_data.channels.num;
switch (ao_data.format & AF_FORMAT_BITS_MASK) {
case AF_FORMAT_8BIT:
break;
@@ -459,10 +465,10 @@ static void reset(void){
ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
ioctl (audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
if(!AF_FORMAT_IS_AC3(ao_data.format)) {
- if (ao_data.channels > 2)
- ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels);
+ if (ao_data.channels.num > 2)
+ ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels.num);
else {
- int c = ao_data.channels-1;
+ int c = ao_data.channels.num-1;
ioctl (audio_fd, SNDCTL_DSP_STEREO, &c);
}
ioctl (audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c
index 5b3cc94395..903e89bc8b 100644
--- a/audio/out/ao_pcm.c
+++ b/audio/out/ao_pcm.c
@@ -69,7 +69,7 @@ static void fput32le(uint32_t val, FILE *fp)
static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
{
- bool use_waveex = ao->channels >= 5 && ao->channels <= 8;
+ bool use_waveex = true;
uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ?
WAV_ID_FLOAT_PCM : WAV_ID_PCM;
uint32_t fmt_chunk_size = use_waveex ? 40 : 16;
@@ -86,30 +86,17 @@ static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
fput32le(WAV_ID_FMT, fp);
fput32le(fmt_chunk_size, fp);
fput16le(use_waveex ? WAV_ID_FORMAT_EXTENSIBLE : fmt, fp);
- fput16le(ao->channels, fp);
+ fput16le(ao->channels.num, fp);
fput32le(ao->samplerate, fp);
fput32le(ao->bps, fp);
- fput16le(ao->channels * (bits / 8), fp);
+ fput16le(ao->channels.num * (bits / 8), fp);
fput16le(bits, fp);
if (use_waveex) {
// Extension chunk
fput16le(22, fp);
fput16le(bits, fp);
- switch (ao->channels) {
- case 5:
- fput32le(0x0607, fp); // L R C Lb Rb
- break;
- case 6:
- fput32le(0x060f, fp); // L R C Lb Rb LFE
- break;
- case 7:
- fput32le(0x0727, fp); // L R C Cb Ls Rs LFE
- break;
- case 8:
- fput32le(0x063f, fp); // L R C Lb Rb Ls Rs LFE
- break;
- }
+ fput32le(mp_chmap_to_waveext(&ao->channels), fp);
// 2 bytes format + 14 bytes guid
fput32le(fmt, fp);
fput32le(0x00100000, fp);
@@ -159,14 +146,19 @@ static int init(struct ao *ao, char *params)
}
}
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ return -1;
+
ao->outburst = 65536;
- ao->bps = ao->channels * ao->samplerate * (af_fmt2bits(ao->format) / 8);
+ ao->bps = ao->channels.num * ao->samplerate * (af_fmt2bits(ao->format) / 8);
mp_tmsg(MSGT_AO, MSGL_INFO, "[AO PCM] File: %s (%s)\n"
"PCM: Samplerate: %d Hz Channels: %d Format: %s\n",
priv->outputfilename,
priv->waveheader ? "WAVE" : "RAW PCM", ao->samplerate,
- ao->channels, af_fmt2str_short(ao->format));
+ ao->channels.num, af_fmt2str_short(ao->format));
mp_tmsg(MSGT_AO, MSGL_INFO,
"[AO PCM] Info: Faster dumping is achieved with -no-video\n"
"[AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).\n");
@@ -222,20 +214,12 @@ static int play(struct ao *ao, void *data, int len, int flags)
{
struct priv *priv = ao->priv;
- if (ao->channels == 5 || ao->channels == 6 || ao->channels == 8) {
- int frame_size = af_fmt2bits(ao->format) / 8;
- len -= len % (frame_size * ao->channels);
- reorder_channel_nch(data, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
- AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT,
- ao->channels, len / frame_size, frame_size);
- }
fwrite(data, len, 1, priv->fp);
priv->data_length += len;
return len;
}
const struct ao_driver audio_out_pcm = {
- .is_new = true,
.info = &(const struct ao_info) {
"RAW PCM/WAVE file writer audio output",
"pcm",
diff --git a/audio/out/ao_portaudio.c b/audio/out/ao_portaudio.c
index b0744e8f8a..f84447bdb4 100644
--- a/audio/out/ao_portaudio.c
+++ b/audio/out/ao_portaudio.c
@@ -273,9 +273,15 @@ static int init(struct ao *ao, char *params)
if (pa_device == paNoDevice)
goto error_exit;
+ // The actual channel order probably depends on the platform.
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext_def(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ goto error_exit;
+
PaStreamParameters sp = {
.device = pa_device,
- .channelCount = ao->channels,
+ .channelCount = ao->channels.num,
.suggestedLatency
= Pa_GetDeviceInfo(pa_device)->defaultHighOutputLatency,
};
@@ -298,7 +304,7 @@ static int init(struct ao *ao, char *params)
ao->format = fmt->mp_format;
sp.sampleFormat = fmt->pa_format;
- priv->framelen = ao->channels * (af_fmt2bits(ao->format) / 8);
+ priv->framelen = ao->channels.num * (af_fmt2bits(ao->format) / 8);
ao->bps = ao->samplerate * priv->framelen;
if (!check_pa_ret(Pa_IsFormatSupported(NULL, &sp, ao->samplerate)))
@@ -413,7 +419,6 @@ static void resume(struct ao *ao)
}
const struct ao_driver audio_out_portaudio = {
- .is_new = true,
.info = &(const struct ao_info) {
"PortAudio",
"portaudio",
diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
index 314ef778bd..15a2b8b0a1 100644
--- a/audio/out/ao_pulse.c
+++ b/audio/out/ao_pulse.c
@@ -143,6 +143,59 @@ static const struct format_map {
{AF_FORMAT_UNKNOWN, 0}
};
+static const int speaker_map[][2] = {
+ {PA_CHANNEL_POSITION_MONO, MP_SPEAKER_ID_FC},
+ {PA_CHANNEL_POSITION_FRONT_LEFT, MP_SPEAKER_ID_FL},
+ {PA_CHANNEL_POSITION_FRONT_RIGHT, MP_SPEAKER_ID_FR},
+ {PA_CHANNEL_POSITION_FRONT_CENTER, MP_SPEAKER_ID_FC},
+ {PA_CHANNEL_POSITION_REAR_CENTER, MP_SPEAKER_ID_BC},
+ {PA_CHANNEL_POSITION_REAR_LEFT, MP_SPEAKER_ID_BL},
+ {PA_CHANNEL_POSITION_REAR_RIGHT, MP_SPEAKER_ID_BR},
+ {PA_CHANNEL_POSITION_LFE, MP_SPEAKER_ID_LFE},
+ {PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, MP_SPEAKER_ID_FLC},
+ {PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, MP_SPEAKER_ID_FRC},
+ {PA_CHANNEL_POSITION_SIDE_LEFT, MP_SPEAKER_ID_SL},
+ {PA_CHANNEL_POSITION_SIDE_RIGHT, MP_SPEAKER_ID_SR},
+ {PA_CHANNEL_POSITION_TOP_CENTER, MP_SPEAKER_ID_TC},
+ {PA_CHANNEL_POSITION_TOP_FRONT_LEFT, MP_SPEAKER_ID_TFL},
+ {PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, MP_SPEAKER_ID_TFR},
+ {PA_CHANNEL_POSITION_TOP_FRONT_CENTER, MP_SPEAKER_ID_TFC},
+ {PA_CHANNEL_POSITION_TOP_REAR_LEFT, MP_SPEAKER_ID_TBL},
+ {PA_CHANNEL_POSITION_TOP_REAR_RIGHT, MP_SPEAKER_ID_TBR},
+ {PA_CHANNEL_POSITION_TOP_REAR_CENTER, MP_SPEAKER_ID_TBC},
+ {PA_CHANNEL_POSITION_INVALID, -1}
+};
+
+static bool chmap_pa_from_mp(pa_channel_map *dst, struct mp_chmap *src)
+{
+ if (src->num > PA_CHANNELS_MAX)
+ return false;
+ dst->channels = src->num;
+ for (int n = 0; n < src->num; n++) {
+ int mp_speaker = src->speaker[n];
+ int pa_speaker = PA_CHANNEL_POSITION_INVALID;
+ for (int i = 0; speaker_map[i][1] != -1; i++) {
+ if (speaker_map[i][1] == mp_speaker) {
+ pa_speaker = speaker_map[i][0];
+ break;
+ }
+ }
+ if (pa_speaker == PA_CHANNEL_POSITION_INVALID)
+ return false;
+ dst->map[n] = pa_speaker;
+ }
+ return true;
+}
+
+static bool select_chmap(struct ao *ao, pa_channel_map *dst)
+{
+ struct mp_chmap_sel sel = {0};
+ for (int n = 0; speaker_map[n][1] != -1; n++)
+ mp_chmap_sel_add_speaker(&sel, speaker_map[n][1]);
+ return ao_chmap_sel_adjust(ao, &sel, &ao->channels) &&
+ chmap_pa_from_mp(dst, &ao->channels);
+}
+
static void uninit(struct ao *ao, bool cut_audio)
{
struct priv *priv = ao->priv;
@@ -209,30 +262,6 @@ static int init(struct ao *ao, char *params)
priv->broken_pause = true;
}
- ss.channels = ao->channels;
- ss.rate = ao->samplerate;
-
- const struct format_map *fmt_map = format_maps;
- while (fmt_map->mp_format != ao->format) {
- if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
- mp_msg(MSGT_AO, MSGL_V,
- "AO: [pulse] Unsupported format, using default\n");
- fmt_map = format_maps;
- break;
- }
- fmt_map++;
- }
- ao->format = fmt_map->mp_format;
- ss.format = fmt_map->pa_format;
-
- if (!pa_sample_spec_valid(&ss)) {
- mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
- goto fail;
- }
-
- pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
- ao->bps = pa_bytes_per_second(&ss);
-
if (!(priv->mainloop = pa_threaded_mainloop_new())) {
mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
goto fail;
@@ -260,6 +289,32 @@ static int init(struct ao *ao, char *params)
if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
goto unlock_and_fail;
+ ss.channels = ao->channels.num;
+ ss.rate = ao->samplerate;
+
+ const struct format_map *fmt_map = format_maps;
+ while (fmt_map->mp_format != ao->format) {
+ if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
+ mp_msg(MSGT_AO, MSGL_V,
+ "AO: [pulse] Unsupported format, using default\n");
+ fmt_map = format_maps;
+ break;
+ }
+ fmt_map++;
+ }
+ ao->format = fmt_map->mp_format;
+ ss.format = fmt_map->pa_format;
+
+ if (!pa_sample_spec_valid(&ss)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
+ goto fail;
+ }
+
+ if (!select_chmap(ao, &map))
+ goto fail;
+
+ ao->bps = pa_bytes_per_second(&ss);
+
if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss,
&map)))
goto unlock_and_fail;
@@ -495,7 +550,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
const ao_control_vol_t *vol = arg;
struct pa_cvolume volume;
- pa_cvolume_reset(&volume, ao->channels);
+ pa_cvolume_reset(&volume, ao->channels.num);
if (volume.channels != 2)
pa_cvolume_set(&volume, volume.channels, VOL_MP2PA(vol->left));
else {
@@ -533,7 +588,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
}
const struct ao_driver audio_out_pulse = {
- .is_new = true,
.info = &(const struct ao_info) {
"PulseAudio audio output",
"pulse",
diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c
index 7722bf19d2..78ea2c63f5 100644
--- a/audio/out/ao_rsound.c
+++ b/audio/out/ao_rsound.c
@@ -121,8 +121,16 @@ static int init(struct ao *ao, char *params)
free(port);
}
+ // Actual channel layout unknown.
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext_def(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
+ rsd_free(priv->rd);
+ return -1;
+ }
+
rsd_set_param(priv->rd, RSD_SAMPLERATE, &ao->samplerate);
- rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels);
+ rsd_set_param(priv->rd, RSD_CHANNELS, &ao->channels.num);
int rsd_format = set_format(ao);
rsd_set_param(priv->rd, RSD_FORMAT, &rsd_format);
@@ -132,7 +140,7 @@ static int init(struct ao *ao, char *params)
return -1;
}
- ao->bps = ao->channels * ao->samplerate * af_fmt2bits(ao->format) / 8;
+ ao->bps = ao->channels.num * ao->samplerate * af_fmt2bits(ao->format) / 8;
return 0;
}
@@ -189,7 +197,6 @@ static float get_delay(struct ao *ao)
}
const struct ao_driver audio_out_rsound = {
- .is_new = true,
.info = &(const struct ao_info) {
.name = "RSound output driver",
.short_name = "rsound",
diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c
index 7cfb1ae1e2..6678cd3bd3 100644
--- a/audio/out/ao_sdl.c
+++ b/audio/out/ao_sdl.c
@@ -160,6 +160,13 @@ static int init(struct ao *ao, char *params)
return -1;
}
+ struct mp_chmap_sel sel = {0};
+ mp_chmap_sel_add_waveext_def(&sel);
+ if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
+ uninit(ao, true);
+ return -1;
+ }
+
SDL_AudioSpec desired, obtained;
int bytes = 0;
@@ -185,7 +192,7 @@ static int init(struct ao *ao, char *params)
#endif
}
desired.freq = ao->samplerate;
- desired.channels = ao->channels;
+ desired.channels = ao->channels.num;
desired.samples = FFMIN(32768, ceil_power_of_two(ao->samplerate * buflen));
desired.callback = audio_callback;
desired.userdata = ao;
@@ -236,9 +243,13 @@ static int init(struct ao *ao, char *params)
return -1;
}
+ if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) {
+ uninit(ao, true);
+ return -1;
+ }
+
ao->samplerate = obtained.freq;
- ao->channels = obtained.channels;
- ao->bps = ao->channels * ao->samplerate * bytes;
+ ao->bps = ao->channels.num * ao->samplerate * bytes;
ao->buffersize = obtained.size * bufcnt;
ao->outburst = obtained.size;
priv->buffer = av_fifo_alloc(ao->buffersize);
@@ -362,7 +373,6 @@ static float get_delay(struct ao *ao)
}
const struct ao_driver audio_out_sdl = {
- .is_new = true,
.info = &(const struct ao_info) {
"SDL Audio",
"sdl",
diff --git a/audio/out/audio_out_internal.h b/audio/out/audio_out_internal.h
index f3e92dff66..7b863cfd21 100644
--- a/audio/out/audio_out_internal.h
+++ b/audio/out/audio_out_internal.h
@@ -20,11 +20,12 @@
#define MPLAYER_AUDIO_OUT_INTERNAL_H
#include "core/options.h"
+#include "ao.h"
// prototypes:
//static ao_info_t info;
static int control(int cmd, void *arg);
-static int init(int rate,int channels,int format,int flags);
+static int init(int rate,const struct mp_chmap *channels,int format,int flags);
static void uninit(int immed);
static void reset(void);
static int get_space(void);
diff --git a/audio/reorder_ch.c b/audio/reorder_ch.c
index 72849c3d13..3a2b747668 100644
--- a/audio/reorder_ch.c
+++ b/audio/reorder_ch.c
@@ -20,1384 +20,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <stdio.h>
-#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "audio/reorder_ch.h"
-#ifdef TEST
-#define mp_msg(mod,lev, fmt, args... ) printf( fmt, ## args )
-#else
-#include "core/mp_msg.h"
-#endif
-
-
-#define REORDER_COPY_5(DEST,SRC,SAMPLES,S0,S1,S2,S3,S4) \
-for (i = 0; i < SAMPLES; i += 5) {\
- DEST[i] = SRC[i+S0];\
- DEST[i+1] = SRC[i+S1];\
- DEST[i+2] = SRC[i+S2];\
- DEST[i+3] = SRC[i+S3];\
- DEST[i+4] = SRC[i+S4];\
-}
-
-static int reorder_copy_5ch(void *dest, const void *src,
- unsigned int samples, unsigned int samplesize,
- int s0, int s1, int s2, int s3, int s4)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *dest_8 = dest;
- const int8_t *src_8 = src;
- REORDER_COPY_5(dest_8,src_8,samples,s0,s1,s2,s3,s4);
- break;
- }
- case 2:
- {
- int16_t *dest_16 = dest;
- const int16_t *src_16 = src;
- REORDER_COPY_5(dest_16,src_16,samples,s0,s1,s2,s3,s4);
- break;
- }
- case 3:
- {
- int8_t *dest_8 = dest;
- const int8_t *src_8 = src;
- for (i = 0; i < samples * 3; i += 15) {
- dest_8[i] = src_8[i+s0*3];
- dest_8[i+1] = src_8[i+s0*3+1];
- dest_8[i+2] = src_8[i+s0*3+2];
- dest_8[i+3] = src_8[i+s1*3];
- dest_8[i+4] = src_8[i+s1*3+1];
- dest_8[i+5] = src_8[i+s1*3+2];
- dest_8[i+6] = src_8[i+s2*3];
- dest_8[i+7] = src_8[i+s2*3+1];
- dest_8[i+8] = src_8[i+s2*3+2];
- dest_8[i+9] = src_8[i+s3*3];
- dest_8[i+10] = src_8[i+s3*3+1];
- dest_8[i+11] = src_8[i+s3*3+2];
- dest_8[i+12] = src_8[i+s4*3];
- dest_8[i+13] = src_8[i+s4*3+1];
- dest_8[i+14] = src_8[i+s4*3+2];
- }
- break;
- }
- case 4:
- {
- int32_t *dest_32 = dest;
- const int32_t *src_32 = src;
- REORDER_COPY_5(dest_32,src_32,samples,s0,s1,s2,s3,s4);
- break;
- }
- case 8:
- {
- int64_t *dest_64 = dest;
- const int64_t *src_64 = src;
- REORDER_COPY_5(dest_64,src_64,samples,s0,s1,s2,s3,s4);
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_COPY_6(DEST,SRC,SAMPLES,S0,S1,S2,S3,S4,S5) \
-for (i = 0; i < SAMPLES; i += 6) {\
- DEST[i] = SRC[i+S0];\
- DEST[i+1] = SRC[i+S1];\
- DEST[i+2] = SRC[i+S2];\
- DEST[i+3] = SRC[i+S3];\
- DEST[i+4] = SRC[i+S4];\
- DEST[i+5] = SRC[i+S5];\
-}
-
-static int reorder_copy_6ch(void *dest, const void *src,
- unsigned int samples, uint8_t samplesize,
- int s0, int s1, int s2, int s3, int s4, int s5)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *dest_8 = dest;
- const int8_t *src_8 = src;
- REORDER_COPY_6(dest_8,src_8,samples,s0,s1,s2,s3,s4,s5);
- break;
- }
- case 2:
- {
- int16_t *dest_16 = dest;
- const int16_t *src_16 = src;
- REORDER_COPY_6(dest_16,src_16,samples,s0,s1,s2,s3,s4,s5);
- break;
- }
- case 3:
- {
- int8_t *dest_8 = dest;
- const int8_t *src_8 = src;
- for (i = 0; i < samples * 3; i += 18) {
- dest_8[i] = src_8[i+s0*3];
- dest_8[i+1] = src_8[i+s0*3+1];
- dest_8[i+2] = src_8[i+s0*3+2];
- dest_8[i+3] = src_8[i+s1*3];
- dest_8[i+4] = src_8[i+s1*3+1];
- dest_8[i+5] = src_8[i+s1*3+2];
- dest_8[i+6] = src_8[i+s2*3];
- dest_8[i+7] = src_8[i+s2*3+1];
- dest_8[i+8] = src_8[i+s2*3+2];
- dest_8[i+9] = src_8[i+s3*3];
- dest_8[i+10] = src_8[i+s3*3+1];
- dest_8[i+11] = src_8[i+s3*3+2];
- dest_8[i+12] = src_8[i+s4*3];
- dest_8[i+13] = src_8[i+s4*3+1];
- dest_8[i+14] = src_8[i+s4*3+2];
- dest_8[i+15] = src_8[i+s5*3];
- dest_8[i+16] = src_8[i+s5*3+1];
- dest_8[i+17] = src_8[i+s5*3+2];
- }
- break;
- }
- case 4:
- {
- int32_t *dest_32 = dest;
- const int32_t *src_32 = src;
- REORDER_COPY_6(dest_32,src_32,samples,s0,s1,s2,s3,s4,s5);
- break;
- }
- case 8:
- {
- int64_t *dest_64 = dest;
- const int64_t *src_64 = src;
- REORDER_COPY_6(dest_64,src_64,samples,s0,s1,s2,s3,s4,s5);
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_COPY_8(DEST,SRC,SAMPLES,S0,S1,S2,S3,S4,S5,S6,S7) \
-for (i = 0; i < SAMPLES; i += 8) {\
- DEST[i] = SRC[i+S0];\
- DEST[i+1] = SRC[i+S1];\
- DEST[i+2] = SRC[i+S2];\
- DEST[i+3] = SRC[i+S3];\
- DEST[i+4] = SRC[i+S4];\
- DEST[i+5] = SRC[i+S5];\
- DEST[i+6] = SRC[i+S6];\
- DEST[i+7] = SRC[i+S7];\
-}
-
-static int reorder_copy_8ch(void *dest, const void *src,
- unsigned int samples, uint8_t samplesize,
- int s0, int s1, int s2, int s3,
- int s4, int s5, int s6, int s7)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *dest_8 = dest;
- const int8_t *src_8 = src;
- REORDER_COPY_8(dest_8,src_8,samples,s0,s1,s2,s3,s4,s5,s6,s7);
- break;
- }
- case 2:
- {
- int16_t *dest_16 = dest;
- const int16_t *src_16 = src;
- REORDER_COPY_8(dest_16,src_16,samples,s0,s1,s2,s3,s4,s5,s6,s7);
- break;
- }
- case 3:
- {
- int8_t *dest_8 = dest;
- const int8_t *src_8 = src;
- for (i = 0; i < samples * 3; i += 24) {
- dest_8[i] = src_8[i+s0*3];
- dest_8[i+1] = src_8[i+s0*3+1];
- dest_8[i+2] = src_8[i+s0*3+2];
- dest_8[i+3] = src_8[i+s1*3];
- dest_8[i+4] = src_8[i+s1*3+1];
- dest_8[i+5] = src_8[i+s1*3+2];
- dest_8[i+6] = src_8[i+s2*3];
- dest_8[i+7] = src_8[i+s2*3+1];
- dest_8[i+8] = src_8[i+s2*3+2];
- dest_8[i+9] = src_8[i+s3*3];
- dest_8[i+10] = src_8[i+s3*3+1];
- dest_8[i+11] = src_8[i+s3*3+2];
- dest_8[i+12] = src_8[i+s4*3];
- dest_8[i+13] = src_8[i+s4*3+1];
- dest_8[i+14] = src_8[i+s4*3+2];
- dest_8[i+15] = src_8[i+s5*3];
- dest_8[i+16] = src_8[i+s5*3+1];
- dest_8[i+17] = src_8[i+s5*3+2];
- dest_8[i+18] = src_8[i+s6*3];
- dest_8[i+19] = src_8[i+s6*3+1];
- dest_8[i+20] = src_8[i+s6*3+2];
- dest_8[i+21] = src_8[i+s7*3];
- dest_8[i+22] = src_8[i+s7*3+1];
- dest_8[i+23] = src_8[i+s7*3+2];
- }
- break;
- }
- case 4:
- {
- int32_t *dest_32 = dest;
- const int32_t *src_32 = src;
- REORDER_COPY_8(dest_32,src_32,samples,s0,s1,s2,s3,s4,s5,s6,s7);
- break;
- }
- case 8:
- {
- int64_t *dest_64 = dest;
- const int64_t *src_64 = src;
- REORDER_COPY_8(dest_64,src_64,samples,s0,s1,s2,s3,s4,s5,s6,s7);
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-void reorder_channel_copy(void *src,
- int src_layout,
- void *dest,
- int dest_layout,
- int samples,
- int samplesize)
-{
- if (dest_layout==src_layout) {
- memcpy(dest, src, samples*samplesize);
- return;
- }
- if (!AF_IS_SAME_CH_NUM(dest_layout,src_layout)) {
- mp_msg(MSGT_GLOBAL, MSGL_WARN, "[reorder_ch] different channel count "
- "between src and dest: %x, %x\n",
- AF_GET_CH_NUM_WITH_LFE(src_layout),
- AF_GET_CH_NUM_WITH_LFE(dest_layout));
- return;
- }
- switch ((src_layout<<16)|dest_layout) {
- // AF_CHANNEL_LAYOUT_5_0_A L R C Ls Rs
- // AF_CHANNEL_LAYOUT_5_0_B L R Ls Rs C
- // AF_CHANNEL_LAYOUT_5_0_C L C R Ls Rs
- // AF_CHANNEL_LAYOUT_5_0_D C L R Ls Rs
- case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_B:
- reorder_copy_5ch(dest, src, samples, samplesize, 0, 1, 3, 4, 2);
- break;
- case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_C:
- reorder_copy_5ch(dest, src, samples, samplesize, 0, 2, 1, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_D:
- reorder_copy_5ch(dest, src, samples, samplesize, 2, 0, 1, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_A:
- reorder_copy_5ch(dest, src, samples, samplesize, 0, 1, 4, 2, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_C:
- reorder_copy_5ch(dest, src, samples, samplesize, 0, 4, 1, 2, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_D:
- reorder_copy_5ch(dest, src, samples, samplesize, 4, 0, 1, 2, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_A:
- reorder_copy_5ch(dest, src, samples, samplesize, 0, 2, 1, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_B:
- reorder_copy_5ch(dest, src, samples, samplesize, 0, 2, 3, 4, 1);
- break;
- case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_D:
- reorder_copy_5ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_A:
- reorder_copy_5ch(dest, src, samples, samplesize, 1, 2, 0, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_B:
- reorder_copy_5ch(dest, src, samples, samplesize, 1, 2, 3, 4, 0);
- break;
- case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_C:
- reorder_copy_5ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4);
- break;
- // AF_CHANNEL_LAYOUT_5_1_A L R C LFE Ls Rs
- // AF_CHANNEL_LAYOUT_5_1_B L R Ls Rs C LFE
- // AF_CHANNEL_LAYOUT_5_1_C L C R Ls Rs LFE
- // AF_CHANNEL_LAYOUT_5_1_D C L R Ls Rs LFE
- // AF_CHANNEL_LAYOUT_5_1_E LFE L C R Ls Rs
- case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_copy_6ch(dest, src, samples, samplesize, 0, 1, 4, 5, 2, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_C:
- reorder_copy_6ch(dest, src, samples, samplesize, 0, 2, 1, 4, 5, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_D:
- reorder_copy_6ch(dest, src, samples, samplesize, 2, 0, 1, 4, 5, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_A:
- reorder_copy_6ch(dest, src, samples, samplesize, 0, 1, 4, 5, 2, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_C:
- reorder_copy_6ch(dest, src, samples, samplesize, 0, 4, 1, 2, 3, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_D:
- reorder_copy_6ch(dest, src, samples, samplesize, 4, 0, 1, 2, 3, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_E:
- reorder_copy_6ch(dest, src, samples, samplesize, 5, 0, 4, 1, 2, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_A:
- reorder_copy_6ch(dest, src, samples, samplesize, 0, 2, 1, 5, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_copy_6ch(dest, src, samples, samplesize, 0, 2, 3, 4, 1, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_D:
- reorder_copy_6ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_A:
- reorder_copy_6ch(dest, src, samples, samplesize, 1, 2, 0, 5, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_copy_6ch(dest, src, samples, samplesize, 1, 2, 3, 4, 0, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_C:
- reorder_copy_6ch(dest, src, samples, samplesize, 1, 0, 2, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_E << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_copy_6ch(dest, src, samples, samplesize, 1, 3, 4, 5, 2, 0);
- break;
- case AF_CHANNEL_LAYOUT_5_1_F << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_copy_6ch(dest, src, samples, samplesize, 1, 2, 4, 5, 0, 3);
- break;
- // AF_CHANNEL_LAYOUT_7_1_A L R C LFE Ls Rs Rls Rrs
- // AF_CHANNEL_LAYOUT_7_1_B L R Ls Rs C LFE Rls Rrs
- // AF_CHANNEL_LAYOUT_7_1_D C L R Ls Rs Rls Rrs LFE
- case AF_CHANNEL_LAYOUT_7_1_A << 16 | AF_CHANNEL_LAYOUT_7_1_B:
- case AF_CHANNEL_LAYOUT_7_1_B << 16 | AF_CHANNEL_LAYOUT_7_1_A:
- reorder_copy_8ch(dest, src, samples, samplesize, 0, 1, 4, 5, 2, 3, 6, 7);
- break;
- case AF_CHANNEL_LAYOUT_7_1_D << 16 | AF_CHANNEL_LAYOUT_7_1_B:
- reorder_copy_8ch(dest, src, samples, samplesize, 1, 2, 3, 4, 0, 7, 5, 6);
- break;
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN, "[reorder_channel_copy] unsupport "
- "from %x to %x, %d * %d\n", src_layout, dest_layout,
- samples, samplesize);
- memcpy(dest, src, samples*samplesize);
- }
-}
-
-
-#define REORDER_SELF_SWAP_2(SRC,TMP,SAMPLES,CHNUM,S0,S1) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = TMP;\
-}
-
-static int reorder_self_2(void *src, unsigned int samples,
- unsigned int samplesize, unsigned int chnum,
- int s0, int s1)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2(src_8,tmp,samples,6,s0,s1);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_2(src_8,tmp,samples,8,s0,s1);
- }
- else {
- REORDER_SELF_SWAP_2(src_8,tmp,samples,5,s0,s1);
- }
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2(src_16,tmp,samples,6,s0,s1);
- }
- else if (chnum==3) {
- REORDER_SELF_SWAP_2(src_16,tmp,samples,3,s0,s1);
- }
- else if (chnum==4) {
- REORDER_SELF_SWAP_2(src_16,tmp,samples,3,s0,s1);
- }
- else {
- REORDER_SELF_SWAP_2(src_16,tmp,samples,5,s0,s1);
- }
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += chnum * 3) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = tmp0;
- src_8[i+s1*3+1] = tmp1;
- src_8[i+s1*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2(src_32,tmp,samples,6,s0,s1);
- }
- else if (chnum==3) {
- REORDER_SELF_SWAP_2(src_32,tmp,samples,3,s0,s1);
- }
- else if (chnum==4) {
- REORDER_SELF_SWAP_2(src_32,tmp,samples,4,s0,s1);
- }
- else {
- REORDER_SELF_SWAP_2(src_32,tmp,samples,5,s0,s1);
- }
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2(src_64,tmp,samples,6,s0,s1);
- }
- else if (chnum==3) {
- REORDER_SELF_SWAP_2(src_64,tmp,samples,3,s0,s1);
- }
- else if (chnum==4) {
- REORDER_SELF_SWAP_2(src_64,tmp,samples,4,s0,s1);
- }
- else {
- REORDER_SELF_SWAP_2(src_64,tmp,samples,5,s0,s1);
- }
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_3(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = SRC[i+S2];\
- SRC[i+S2] = TMP;\
-}
-
-static int reorder_self_3(void *src, unsigned int samples,
- unsigned int samplesize, unsigned int chnum,
- int s0, int s1, int s2)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_3(src_8,tmp,samples,6,s0,s1,s2);
- }
- else {
- REORDER_SELF_SWAP_3(src_8,tmp,samples,5,s0,s1,s2);
- }
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_3(src_16,tmp,samples,6,s0,s1,s2);
- }
- else {
- REORDER_SELF_SWAP_3(src_16,tmp,samples,5,s0,s1,s2);
- }
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += chnum * 3) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = src_8[i+s2*3];
- src_8[i+s1*3+1] = src_8[i+s2*3+1];
- src_8[i+s1*3+2] = src_8[i+s2*3+2];
- src_8[i+s2*3] = tmp0;
- src_8[i+s2*3+1] = tmp1;
- src_8[i+s2*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_3(src_32,tmp,samples,6,s0,s1,s2);
- }
- else {
- REORDER_SELF_SWAP_3(src_32,tmp,samples,5,s0,s1,s2);
- }
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_3(src_64,tmp,samples,6,s0,s1,s2);
- }
- else {
- REORDER_SELF_SWAP_3(src_64,tmp,samples,5,s0,s1,s2);
- }
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_4_STEP_1(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = SRC[i+S2];\
- SRC[i+S2] = SRC[i+S3];\
- SRC[i+S3] = TMP;\
-}
-
-static int reorder_self_4_step_1(void *src, unsigned int samples,
- unsigned int samplesize, unsigned int chnum,
- int s0, int s1, int s2, int s3)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_4_STEP_1(src_8,tmp,samples,6,s0,s1,s2,s3);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_4_STEP_1(src_8,tmp,samples,8,s0,s1,s2,s3);
- }
- else {
- REORDER_SELF_SWAP_4_STEP_1(src_8,tmp,samples,5,s0,s1,s2,s3);
- }
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_4_STEP_1(src_16,tmp,samples,6,s0,s1,s2,s3);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_4_STEP_1(src_16,tmp,samples,8,s0,s1,s2,s3);
- }
- else {
- REORDER_SELF_SWAP_4_STEP_1(src_16,tmp,samples,5,s0,s1,s2,s3);
- }
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += chnum * 3) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = src_8[i+s2*3];
- src_8[i+s1*3+1] = src_8[i+s2*3+1];
- src_8[i+s1*3+2] = src_8[i+s2*3+2];
- src_8[i+s2*3] = src_8[i+s3*3];
- src_8[i+s2*3+1] = src_8[i+s3*3+1];
- src_8[i+s2*3+2] = src_8[i+s3*3+2];
- src_8[i+s3*3] = tmp0;
- src_8[i+s3*3+1] = tmp1;
- src_8[i+s3*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_4_STEP_1(src_32,tmp,samples,6,s0,s1,s2,s3);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_4_STEP_1(src_32,tmp,samples,8,s0,s1,s2,s3);
- }
- else {
- REORDER_SELF_SWAP_4_STEP_1(src_32,tmp,samples,5,s0,s1,s2,s3);
- }
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_4_STEP_1(src_64,tmp,samples,6,s0,s1,s2,s3);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_4_STEP_1(src_64,tmp,samples,8,s0,s1,s2,s3);
- }
- else {
- REORDER_SELF_SWAP_4_STEP_1(src_64,tmp,samples,5,s0,s1,s2,s3);
- }
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_4_STEP_2(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S2];\
- SRC[i+S2] = TMP;\
- TMP = SRC[i+S1];\
- SRC[i+S1] = SRC[i+S3];\
- SRC[i+S3] = TMP;\
-}
-
-static int reorder_self_4_step_2(void *src, unsigned int samples,
- unsigned int samplesize, unsigned int chnum,
- int s0, int s1, int s2, int s3)
-{
- int i;
- switch (samplesize) {
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += chnum * 3) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s2*3];
- src_8[i+s0*3+1] = src_8[i+s2*3+1];
- src_8[i+s0*3+2] = src_8[i+s2*3+2];
- src_8[i+s2*3] = tmp0;
- src_8[i+s2*3+1] = tmp1;
- src_8[i+s2*3+2] = tmp2;
- tmp0 = src_8[i+s1*3];
- tmp1 = src_8[i+s1*3+1];
- tmp2 = src_8[i+s1*3+2];
- src_8[i+s1*3] = src_8[i+s3*3];
- src_8[i+s1*3+1] = src_8[i+s3*3+1];
- src_8[i+s1*3+2] = src_8[i+s3*3+2];
- src_8[i+s3*3] = tmp0;
- src_8[i+s3*3+1] = tmp1;
- src_8[i+s3*3+2] = tmp2;
- }
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_5_STEP_1(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = SRC[i+S2];\
- SRC[i+S2] = SRC[i+S3];\
- SRC[i+S3] = SRC[i+S4];\
- SRC[i+S4] = TMP;\
-}
-
-static int reorder_self_5_step_1(void *src, unsigned int samples,
- unsigned int samplesize, unsigned int chnum,
- int s0, int s1, int s2, int s3, int s4)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_5_STEP_1(src_8,tmp,samples,6,s0,s1,s2,s3,s4);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_5_STEP_1(src_8,tmp,samples,8,s0,s1,s2,s3,s4);
- }
- else {
- REORDER_SELF_SWAP_5_STEP_1(src_8,tmp,samples,5,s0,s1,s2,s3,s4);
- }
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_5_STEP_1(src_16,tmp,samples,6,s0,s1,s2,s3,s4);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_5_STEP_1(src_16,tmp,samples,8,s0,s1,s2,s3,s4);
- }
- else {
- REORDER_SELF_SWAP_5_STEP_1(src_16,tmp,samples,5,s0,s1,s2,s3,s4);
- }
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += chnum * 3) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = src_8[i+s2*3];
- src_8[i+s1*3+1] = src_8[i+s2*3+1];
- src_8[i+s1*3+2] = src_8[i+s2*3+2];
- src_8[i+s2*3] = src_8[i+s3*3];
- src_8[i+s2*3+1] = src_8[i+s3*3+1];
- src_8[i+s2*3+2] = src_8[i+s3*3+2];
- src_8[i+s3*3] = src_8[i+s4*3];
- src_8[i+s3*3+1] = src_8[i+s4*3+1];
- src_8[i+s3*3+2] = src_8[i+s4*3+2];
- src_8[i+s4*3] = tmp0;
- src_8[i+s4*3+1] = tmp1;
- src_8[i+s4*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_5_STEP_1(src_32,tmp,samples,6,s0,s1,s2,s3,s4);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_5_STEP_1(src_32,tmp,samples,8,s0,s1,s2,s3,s4);
- }
- else {
- REORDER_SELF_SWAP_5_STEP_1(src_32,tmp,samples,5,s0,s1,s2,s3,s4);
- }
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_5_STEP_1(src_64,tmp,samples,6,s0,s1,s2,s3,s4);
- }
- else if (chnum==8) {
- REORDER_SELF_SWAP_5_STEP_1(src_64,tmp,samples,8,s0,s1,s2,s3,s4);
- }
- else {
- REORDER_SELF_SWAP_5_STEP_1(src_64,tmp,samples,5,s0,s1,s2,s3,s4);
- }
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_2_3(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = TMP;\
- TMP = SRC[i+S2];\
- SRC[i+S2] = SRC[i+S3];\
- SRC[i+S3] = SRC[i+S4];\
- SRC[i+S4] = TMP;\
-}
-
-static int reorder_self_2_3(void *src, unsigned int samples,
- unsigned int samplesize,
- int s0, int s1, int s2, int s3, int s4)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- REORDER_SELF_SWAP_2_3(src_8,tmp,samples,6,s0,s1,s2,s3,s4);
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- REORDER_SELF_SWAP_2_3(src_16,tmp,samples,6,s0,s1,s2,s3,s4);
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += 18) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = tmp0;
- src_8[i+s1*3+1] = tmp1;
- src_8[i+s1*3+2] = tmp2;
- tmp0 = src_8[i+s2*3];
- tmp1 = src_8[i+s2*3+1];
- tmp2 = src_8[i+s2*3+2];
- src_8[i+s2*3] = src_8[i+s3*3];
- src_8[i+s2*3+1] = src_8[i+s3*3+1];
- src_8[i+s2*3+2] = src_8[i+s3*3+2];
- src_8[i+s3*3] = src_8[i+s4*3];
- src_8[i+s3*3+1] = src_8[i+s4*3+1];
- src_8[i+s3*3+2] = src_8[i+s4*3+2];
- src_8[i+s4*3] = tmp0;
- src_8[i+s4*3+1] = tmp1;
- src_8[i+s4*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- REORDER_SELF_SWAP_2_3(src_32,tmp,samples,6,s0,s1,s2,s3,s4);
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- REORDER_SELF_SWAP_2_3(src_64,tmp,samples,6,s0,s1,s2,s3,s4);
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_3_3(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4,S5) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = SRC[i+S2];\
- SRC[i+S2] = TMP;\
- TMP = SRC[i+S3];\
- SRC[i+S3] = SRC[i+S4];\
- SRC[i+S4] = SRC[i+S5];\
- SRC[i+S5] = TMP;\
-}
-
-static int reorder_self_3_3(void *src, unsigned int samples,
- unsigned int samplesize,
- int s0, int s1, int s2, int s3, int s4, int s5)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- REORDER_SELF_SWAP_3_3(src_8,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- REORDER_SELF_SWAP_3_3(src_16,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += 18) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = src_8[i+s2*3];
- src_8[i+s1*3+1] = src_8[i+s2*3+1];
- src_8[i+s1*3+2] = src_8[i+s2*3+2];
- src_8[i+s2*3] = tmp0;
- src_8[i+s2*3+1] = tmp1;
- src_8[i+s2*3+2] = tmp2;
- tmp0 = src_8[i+s3*3];
- tmp1 = src_8[i+s3*3+1];
- tmp2 = src_8[i+s3*3+2];
- src_8[i+s3*3] = src_8[i+s4*3];
- src_8[i+s3*3+1] = src_8[i+s4*3+1];
- src_8[i+s3*3+2] = src_8[i+s4*3+2];
- src_8[i+s4*3] = src_8[i+s5*3];
- src_8[i+s4*3+1] = src_8[i+s5*3+1];
- src_8[i+s4*3+2] = src_8[i+s5*3+2];
- src_8[i+s5*3] = tmp0;
- src_8[i+s5*3+1] = tmp1;
- src_8[i+s5*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- REORDER_SELF_SWAP_3_3(src_32,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- REORDER_SELF_SWAP_3_3(src_64,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-#define REORDER_SELF_SWAP_2_4(SRC,TMP,SAMPLES,CHNUM,S0,S1,S2,S3,S4,S5) \
-for (i = 0; i < SAMPLES; i += CHNUM) {\
- TMP = SRC[i+S0];\
- SRC[i+S0] = SRC[i+S1];\
- SRC[i+S1] = TMP;\
- TMP = SRC[i+S2];\
- SRC[i+S2] = SRC[i+S3];\
- SRC[i+S3] = SRC[i+S4];\
- SRC[i+S4] = SRC[i+S5];\
- SRC[i+S5] = TMP;\
-}
-
-static int reorder_self_2_4(void *src, unsigned int samples,
- unsigned int samplesize, int chnum,
- int s0, int s1, int s2, int s3, int s4, int s5)
-{
- int i;
- switch (samplesize) {
- case 1:
- {
- int8_t *src_8 = src;
- int8_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2_4(src_8,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- } else {
- REORDER_SELF_SWAP_2_4(src_8,tmp,samples,8,s0,s1,s2,s3,s4,s5);
- }
- break;
- }
- case 2:
- {
- int16_t *src_16 = src;
- int16_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2_4(src_16,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- } else {
- REORDER_SELF_SWAP_2_4(src_16,tmp,samples,8,s0,s1,s2,s3,s4,s5);
- }
- break;
- }
- case 3:
- {
- int8_t *src_8 = src;
- int8_t tmp0, tmp1, tmp2;
- for (i = 0; i < samples * 3; i += chnum * 3) {
- tmp0 = src_8[i+s0*3];
- tmp1 = src_8[i+s0*3+1];
- tmp2 = src_8[i+s0*3+2];
- src_8[i+s0*3] = src_8[i+s1*3];
- src_8[i+s0*3+1] = src_8[i+s1*3+1];
- src_8[i+s0*3+2] = src_8[i+s1*3+2];
- src_8[i+s1*3] = tmp0;
- src_8[i+s1*3+1] = tmp1;
- src_8[i+s1*3+2] = tmp2;
- tmp0 = src_8[i+s2*3];
- tmp1 = src_8[i+s2*3+1];
- tmp2 = src_8[i+s2*3+2];
- src_8[i+s2*3] = src_8[i+s3*3];
- src_8[i+s2*3+1] = src_8[i+s3*3+1];
- src_8[i+s2*3+2] = src_8[i+s3*3+2];
- src_8[i+s3*3] = src_8[i+s4*3];
- src_8[i+s3*3+1] = src_8[i+s4*3+1];
- src_8[i+s3*3+2] = src_8[i+s4*3+2];
- src_8[i+s4*3] = src_8[i+s5*3];
- src_8[i+s4*3+1] = src_8[i+s5*3+1];
- src_8[i+s4*3+2] = src_8[i+s5*3+2];
- src_8[i+s5*3] = tmp0;
- src_8[i+s5*3+1] = tmp1;
- src_8[i+s5*3+2] = tmp2;
- }
- break;
- }
- case 4:
- {
- int32_t *src_32 = src;
- int32_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2_4(src_32,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- } else {
- REORDER_SELF_SWAP_2_4(src_32,tmp,samples,8,s0,s1,s2,s3,s4,s5);
- }
- break;
- }
- case 8:
- {
- int64_t *src_64 = src;
- int64_t tmp;
- if (chnum==6) {
- REORDER_SELF_SWAP_2_4(src_64,tmp,samples,6,s0,s1,s2,s3,s4,s5);
- } else {
- REORDER_SELF_SWAP_2_4(src_64,tmp,samples,8,s0,s1,s2,s3,s4,s5);
- }
- break;
- }
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_ch] Unsupported sample size: %d, please "
- "report this error on the MPlayer mailing list.\n",samplesize);
- return 0;
- }
- return 1;
-}
-
-void reorder_channel(void *src,
- int src_layout,
- int dest_layout,
- int samples,
- int samplesize)
-{
- if (dest_layout==src_layout)
- return;
- if (!AF_IS_SAME_CH_NUM(dest_layout,src_layout)) {
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_channel] different channel count "
- "between current and target: %x, %x\n",
- AF_GET_CH_NUM_WITH_LFE(src_layout),
- AF_GET_CH_NUM_WITH_LFE(dest_layout));
- return;
- }
- switch ((src_layout<<16)|dest_layout) {
- // AF_CHANNEL_LAYOUT_5_0_A L R C Ls Rs
- // AF_CHANNEL_LAYOUT_5_0_B L R Ls Rs C
- // AF_CHANNEL_LAYOUT_5_0_C L C R Ls Rs
- // AF_CHANNEL_LAYOUT_5_0_D C L R Ls Rs
- case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_B:
- reorder_self_3(src, samples, samplesize, 5, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_C:
- reorder_self_2(src, samples, samplesize, 5, 1, 2);
- break;
- case AF_CHANNEL_LAYOUT_5_0_A << 16 | AF_CHANNEL_LAYOUT_5_0_D:
- reorder_self_3(src, samples, samplesize, 5, 2, 1, 0);
- break;
- case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_A:
- reorder_self_3(src, samples, samplesize, 5, 4, 3, 2);
- break;
- case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_C:
- reorder_self_4_step_1(src, samples, samplesize, 5, 4, 3, 2, 1);
- break;
- case AF_CHANNEL_LAYOUT_5_0_B << 16 | AF_CHANNEL_LAYOUT_5_0_D:
- reorder_self_5_step_1(src, samples, samplesize, 5, 4, 3, 2, 1, 0);
- break;
- case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_A:
- reorder_self_2(src, samples, samplesize, 5, 1, 2);
- break;
- case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_B:
- reorder_self_4_step_1(src, samples, samplesize, 5, 1, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_C << 16 | AF_CHANNEL_LAYOUT_5_0_D:
- reorder_self_2(src, samples, samplesize, 5, 0, 1);
- break;
- case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_A:
- reorder_self_3(src, samples, samplesize, 5, 0, 1, 2);
- break;
- case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_B:
- reorder_self_5_step_1(src, samples, samplesize, 5, 0, 1, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_0_D << 16 | AF_CHANNEL_LAYOUT_5_0_C:
- reorder_self_2(src, samples, samplesize, 5, 0, 1);
- break;
- // AF_CHANNEL_LAYOUT_5_1_A L R C LFE Ls Rs
- // AF_CHANNEL_LAYOUT_5_1_B L R Ls Rs C LFE
- // AF_CHANNEL_LAYOUT_5_1_C L C R Ls Rs LFE
- // AF_CHANNEL_LAYOUT_5_1_D C L R Ls Rs LFE
- // AF_CHANNEL_LAYOUT_5_1_E LFE L C R Ls Rs
- case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- if (samplesize != 3)
- reorder_self_2(src, samples/2, samplesize*2, 3, 1, 2);
- else
- reorder_self_4_step_2(src, samples, samplesize, 6, 2, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_C:
- reorder_self_2_3(src, samples, samplesize, 1, 2, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_A << 16 | AF_CHANNEL_LAYOUT_5_1_D:
- reorder_self_3_3(src, samples, samplesize, 2, 1, 0, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_A:
- if (samplesize != 3)
- reorder_self_2(src, samples/2, samplesize*2, 3, 1, 2);
- else
- reorder_self_4_step_2(src, samples, samplesize, 6, 2, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_C:
- reorder_self_4_step_1(src, samples, samplesize, 6, 4, 3, 2, 1);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_D:
- reorder_self_5_step_1(src, samples, samplesize, 6, 4, 3, 2, 1, 0);
- break;
- case AF_CHANNEL_LAYOUT_5_1_B << 16 | AF_CHANNEL_LAYOUT_5_1_E:
- reorder_self_2_4(src, samples, samplesize, 6, 2, 4, 5, 3, 1, 0);
- break;
- case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_A:
- reorder_self_2_3(src, samples, samplesize, 1, 2, 5, 4, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_self_4_step_1(src, samples, samplesize, 6, 1, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_1_C << 16 | AF_CHANNEL_LAYOUT_5_1_D:
- reorder_self_2(src, samples, samplesize, 6, 0, 1);
- break;
- case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_A:
- reorder_self_3_3(src, samples, samplesize, 0, 1, 2, 5, 4, 3);
- break;
- case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_self_5_step_1(src, samples, samplesize, 6, 0, 1, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_5_1_D << 16 | AF_CHANNEL_LAYOUT_5_1_C:
- reorder_self_2(src, samples, samplesize, 6, 0, 1);
- break;
- case AF_CHANNEL_LAYOUT_5_1_E << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_self_2_4(src, samples, samplesize, 6, 2, 4, 0, 1, 3, 5);
- break;
- case AF_CHANNEL_LAYOUT_5_1_F << 16 | AF_CHANNEL_LAYOUT_5_1_B:
- reorder_self_2_4(src, samples, samplesize, 6, 3, 5, 0, 1, 2, 4);
- break;
- // AF_CHANNEL_LAYOUT_7_1_A L R C LFE Ls Rs Rls Rrs
- // AF_CHANNEL_LAYOUT_7_1_B L R Ls Rs C LFE Rls Rrs
- // AF_CHANNEL_LAYOUT_7_1_C L C R Ls Rs LFE Rls Rrs
- // AF_CHANNEL_LAYOUT_7_1_D C L R Ls Rs Rls Rrs LFE
- // AF_CHANNEL_LAYOUT_7_1_F C L R LFE Ls Rs Rls Rrs
- case AF_CHANNEL_LAYOUT_7_1_A << 16 | AF_CHANNEL_LAYOUT_7_1_B:
- case AF_CHANNEL_LAYOUT_7_1_B << 16 | AF_CHANNEL_LAYOUT_7_1_A:
- if (samplesize != 3)
- reorder_self_2(src, samples/2, samplesize*2, 4, 1, 2);
- else
- reorder_self_4_step_2(src, samples, samplesize, 8, 2, 3, 4, 5);
- break;
- case AF_CHANNEL_LAYOUT_7_1_B << 16 | AF_CHANNEL_LAYOUT_7_1_D:
- // First convert to AF_CHANNEL_LAYOUT_7_1_F
- reorder_self_2_4(src, samples, samplesize, 8, 3, 5, 4, 2, 1, 0);
- // then convert to AF_CHANNEL_LAYOUT_7_1_D
- reorder_self_5_step_1(src, samples, samplesize, 8, 3, 4, 5, 6, 7);
- break;
- case AF_CHANNEL_LAYOUT_7_1_C << 16 | AF_CHANNEL_LAYOUT_7_1_B:
- reorder_self_4_step_1(src, samples, samplesize, 8, 1, 2, 3, 4);
- break;
- case AF_CHANNEL_LAYOUT_7_1_F << 16 | AF_CHANNEL_LAYOUT_7_1_B:
- reorder_self_2_4(src, samples, samplesize, 8, 3, 5, 0, 1, 2, 4);
- break;
- default:
- mp_msg(MSGT_GLOBAL, MSGL_WARN,
- "[reorder_channel] unsupported from %x to %x, %d * %d\n",
- src_layout, dest_layout, samples, samplesize);
- }
-}
-
-
-static int channel_layout_mapping_5ch[AF_CHANNEL_LAYOUT_SOURCE_NUM] = {
- AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT,
- AF_CHANNEL_LAYOUT_AAC_5CH_DEFAULT,
- AF_CHANNEL_LAYOUT_WAVEEX_5CH_DEFAULT,
- AF_CHANNEL_LAYOUT_LAVC_5CH_DEFAULT,
- AF_CHANNEL_LAYOUT_VORBIS_5CH_DEFAULT,
-};
-
-static int channel_layout_mapping_6ch[AF_CHANNEL_LAYOUT_SOURCE_NUM] = {
- AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT,
- AF_CHANNEL_LAYOUT_AAC_6CH_DEFAULT,
- AF_CHANNEL_LAYOUT_WAVEEX_6CH_DEFAULT,
- AF_CHANNEL_LAYOUT_LAVC_6CH_DEFAULT,
- AF_CHANNEL_LAYOUT_VORBIS_6CH_DEFAULT,
-};
-
-static int channel_layout_mapping_8ch[AF_CHANNEL_LAYOUT_SOURCE_NUM] = {
- AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT,
- AF_CHANNEL_LAYOUT_AAC_8CH_DEFAULT,
- AF_CHANNEL_LAYOUT_WAVEEX_8CH_DEFAULT,
- AF_CHANNEL_LAYOUT_LAVC_8CH_DEFAULT,
- AF_CHANNEL_LAYOUT_VORBIS_8CH_DEFAULT,
-};
-
-void reorder_channel_copy_nch(void *src,
- int src_layout,
- void *dest,
- int dest_layout,
- int chnum,
- int samples,
- int samplesize)
-{
- if (chnum < 5 || chnum == 7 || chnum > 8 ||
- src_layout < 0 || dest_layout < 0 ||
- src_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM ||
- dest_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM)
- memcpy(dest, src, samples*samplesize);
- else if (chnum == 6)
- reorder_channel_copy(src, channel_layout_mapping_6ch[src_layout],
- dest, channel_layout_mapping_6ch[dest_layout],
- samples, samplesize);
- else if (chnum == 8)
- reorder_channel_copy(src, channel_layout_mapping_8ch[src_layout],
- dest, channel_layout_mapping_8ch[dest_layout],
- samples, samplesize);
- else
- reorder_channel_copy(src, channel_layout_mapping_5ch[src_layout],
- dest, channel_layout_mapping_5ch[dest_layout],
- samples, samplesize);
-}
-
-void reorder_channel_nch(void *buf,
- int src_layout,
- int dest_layout,
- int chnum,
- int samples,
- int samplesize)
-{
- if (src_layout == dest_layout || chnum < 5 || chnum == 7 || chnum > 8 ||
- src_layout < 0 || dest_layout < 0 ||
- src_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM ||
- dest_layout >= AF_CHANNEL_LAYOUT_SOURCE_NUM ||
- src_layout == dest_layout)
- return;
- if (chnum == 6)
- reorder_channel(buf, channel_layout_mapping_6ch[src_layout],
- channel_layout_mapping_6ch[dest_layout],
- samples, samplesize);
- else if (chnum == 8)
- reorder_channel(buf, channel_layout_mapping_8ch[src_layout],
- channel_layout_mapping_8ch[dest_layout],
- samples, samplesize);
- else
- reorder_channel(buf, channel_layout_mapping_5ch[src_layout],
- channel_layout_mapping_5ch[dest_layout],
- samples, samplesize);
-}
-
-#ifdef TEST
-
-static void test_copy(int channels) {
- int samples = 12*1024*1024;
- int samplesize = 2;
- int i;
- unsigned char *bufin = malloc((samples+100)*samplesize);
- unsigned char *bufout = malloc((samples+100)*samplesize);
- memset(bufin, 0xFF, samples*samplesize);
- for (i = 0;i < 100; ++i)
- reorder_channel_copy(bufin, AF_CHANNEL_LAYOUT_5_1_A,
- bufout, AF_CHANNEL_LAYOUT_5_1_B,
- samples, samplesize);
-// reorder_channel(bufin, AF_CHANNEL_LAYOUT_5_1_B,
-// AF_CHANNEL_LAYOUT_5_1_D,
-// samples, samplesize);
- free(bufin);
- free(bufout);
-}
-
-int main(int argc, char *argv[]) {
- int channels = 6;
- if (argc > 1)
- channels = atoi(argv[1]);
- test_copy(channels);
- return 0;
-}
-
-#endif
-
static inline void reorder_to_planar_(void *restrict out, const void *restrict in,
size_t size, size_t nchan, size_t nmemb)
{
diff --git a/audio/reorder_ch.h b/audio/reorder_ch.h
index 07daa35a08..c9c101e719 100644
--- a/audio/reorder_ch.h
+++ b/audio/reorder_ch.h
@@ -25,114 +25,6 @@
#include <inttypes.h>
-// L - Left
-// R - Right
-// C - Center
-// Ls - Left Surround
-// Rs - Right Surround
-// Cs - Center Surround
-// Rls - Rear Left Surround
-// Rrs - Rear Right Surround
-
-#define AF_LFE (1<<7)
-
-#define AF_CHANNEL_LAYOUT_MONO ((100<<8)|1)
-#define AF_CHANNEL_LAYOUT_STEREO ((101<<8)|2)
-#define AF_CHANNEL_LAYOUT_1_0 AF_CHANNEL_LAYOUT_MONO // C
-#define AF_CHANNEL_LAYOUT_2_0 AF_CHANNEL_LAYOUT_STEREO // L R
-#define AF_CHANNEL_LAYOUT_2_1 ((102<<8)|3) // L R LFE
-#define AF_CHANNEL_LAYOUT_3_0_A ((103<<8)|3) // L R C
-#define AF_CHANNEL_LAYOUT_3_0_B ((104<<8)|3) // C L R
-#define AF_CHANNEL_LAYOUT_4_0_A ((105<<8)|4) // L R C Cs
-#define AF_CHANNEL_LAYOUT_4_0_B ((106<<8)|4) // C L R Cs
-#define AF_CHANNEL_LAYOUT_4_0_C ((107<<8)|4) // L R Ls Rs
-#define AF_CHANNEL_LAYOUT_5_0_A ((108<<8)|5) // L R C Ls Rs
-#define AF_CHANNEL_LAYOUT_5_0_B ((109<<8)|5) // L R Ls Rs C
-#define AF_CHANNEL_LAYOUT_5_0_C ((110<<8)|5) // L C R Ls Rs
-#define AF_CHANNEL_LAYOUT_5_0_D ((111<<8)|5) // C L R Ls Rs
-#define AF_CHANNEL_LAYOUT_5_1_A ((112<<8)|6|AF_LFE) // L R C LFE Ls Rs
-#define AF_CHANNEL_LAYOUT_5_1_B ((113<<8)|6|AF_LFE) // L R Ls Rs C LFE
-#define AF_CHANNEL_LAYOUT_5_1_C ((114<<8)|6|AF_LFE) // L C R Ls Rs LFE
-#define AF_CHANNEL_LAYOUT_5_1_D ((115<<8)|6|AF_LFE) // C L R Ls Rs LFE
-#define AF_CHANNEL_LAYOUT_5_1_E ((116<<8)|6|AF_LFE) // LFE L C R Ls Rs
-#define AF_CHANNEL_LAYOUT_5_1_F ((117<<8)|6|AF_LFE) // C L R LFE Ls Rs
-#define AF_CHANNEL_LAYOUT_6_1_A ((118<<8)|7|AF_LFE) // L R C LFE Ls Rs Cs
-#define AF_CHANNEL_LAYOUT_7_1_A ((119<<8)|8|AF_LFE) // L R C LFE Ls Rs Rls Rrs
-#define AF_CHANNEL_LAYOUT_7_1_B ((120<<8)|8|AF_LFE) // L R Ls Rs C LFE Rls Rrs
-#define AF_CHANNEL_LAYOUT_7_1_C ((121<<8)|8|AF_LFE) // L C R Ls Rs LFE Rls Rrs
-#define AF_CHANNEL_LAYOUT_7_1_D ((122<<8)|8|AF_LFE) // C L R Ls Rs Rls Rrs LFE
-#define AF_CHANNEL_LAYOUT_7_1_F ((123<<8)|8|AF_LFE) // C L R LFE Ls Rs Rls Rrs
-
-
-#define AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_B
-#define AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_B
-#define AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_B
-#define AF_CHANNEL_LAYOUT_MPLAYER_5CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_5CH_DEFAULT
-#define AF_CHANNEL_LAYOUT_MPLAYER_6CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_6CH_DEFAULT
-#define AF_CHANNEL_LAYOUT_MPLAYER_8CH_DEFAULT AF_CHANNEL_LAYOUT_ALSA_8CH_DEFAULT
-#define AF_CHANNEL_LAYOUT_AAC_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_D
-#define AF_CHANNEL_LAYOUT_AAC_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_D
-#define AF_CHANNEL_LAYOUT_AAC_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_D
-#define AF_CHANNEL_LAYOUT_WAVEEX_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_A
-#define AF_CHANNEL_LAYOUT_WAVEEX_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_A
-#define AF_CHANNEL_LAYOUT_WAVEEX_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_A
-#define AF_CHANNEL_LAYOUT_LAVC_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_A
-#define AF_CHANNEL_LAYOUT_LAVC_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_A
-#define AF_CHANNEL_LAYOUT_LAVC_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_A
-#define AF_CHANNEL_LAYOUT_VORBIS_5CH_DEFAULT AF_CHANNEL_LAYOUT_5_0_C
-#define AF_CHANNEL_LAYOUT_VORBIS_6CH_DEFAULT AF_CHANNEL_LAYOUT_5_1_C
-#define AF_CHANNEL_LAYOUT_VORBIS_8CH_DEFAULT AF_CHANNEL_LAYOUT_7_1_C
-
-#define AF_CHANNEL_MASK 0xFF
-#define AF_GET_CH_NUM(A) ((A)&0x7F)
-#define AF_GET_CH_NUM_WITH_LFE(A) ((A)&0xFF)
-#define AF_IS_SAME_CH_NUM(A,B) (((A)&0xFF)==((B)&0xFF))
-#define AF_IS_LAYOUT_SPECIFIED(A) ((A)&0xFFFFF800)
-#define AF_IS_LAYOUT_UNSPECIFIED(A) (!AF_IS_LAYOUT_SPECIFIED(A))
-
-/// Optimized channel reorder between channel layouts with same channel number.
-void reorder_channel_copy(void *src,
- int src_layout,
- void *dest,
- int dest_layout,
- int samples,
- int samplesize);
-
-/// Same with reorder_channel_copy, but works on single buffer.
-void reorder_channel(void *buf,
- int src_layout,
- int dest_layout,
- int samples,
- int samplesize);
-
-// Channel layout definitions for different audio sources or targets
-// When specified channel number, they will be map to the specific layouts.
-#define AF_CHANNEL_LAYOUT_ALSA_DEFAULT 0
-#define AF_CHANNEL_LAYOUT_AAC_DEFAULT 1
-#define AF_CHANNEL_LAYOUT_WAVEEX_DEFAULT 2
-#define AF_CHANNEL_LAYOUT_LAVC_DEFAULT 3
-#define AF_CHANNEL_LAYOUT_VORBIS_DEFAULT 4
-#define AF_CHANNEL_LAYOUT_SOURCE_NUM 5
-#define AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT AF_CHANNEL_LAYOUT_ALSA_DEFAULT
-
-/// Optimized channel reorder between different audio sources and targets.
-void reorder_channel_copy_nch(void *src,
- int src_layout,
- void *dest,
- int dest_layout,
- int chnum,
- int samples,
- int samplesize);
-
-/// Same with reorder_channel_copy_nch, but works on single buffer.
-void reorder_channel_nch(void *buf,
- int src_layout,
- int dest_layout,
- int chnum,
- int samples,
- int samplesize);
-
-/// Utility function for planar audio conversions
void reorder_to_planar(void *restrict out, const void *restrict in,
size_t size, size_t nchan, size_t nmemb);
void reorder_to_packed(uint8_t *out, uint8_t **in,
diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h
index 5b30e387c3..baf0820f48 100644
--- a/core/cfg-mplayer.h
+++ b/core/cfg-mplayer.h
@@ -175,11 +175,6 @@ const m_option_t mfopts_conf[]={
#include "audio/filter/af.h"
extern struct af_cfg af_cfg; // Audio filter configuration, defined in libmpcodecs/dec_audio.c
-const m_option_t audio_filter_conf[]={
- {"list", &af_cfg.list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
- {"force", &af_cfg.force, CONF_TYPE_INT, CONF_RANGE, 0, 7, NULL},
- {NULL, NULL, 0, 0, 0, 0, NULL}
-};
extern int mp_msg_levels[MSGT_MAX];
extern int mp_msg_level_all;
@@ -281,6 +276,7 @@ const m_option_t msgl_config[]={
};
extern const m_option_t lavc_decode_opts_conf[];
+extern const m_option_t ad_lavc_decode_opts_conf[];
#define OPT_BASE_STRUCT struct MPOpts
@@ -418,7 +414,7 @@ const m_option_t common_opts[] = {
// force video/audio rate:
OPT_DOUBLE("fps", force_fps, CONF_MIN, 0),
OPT_INTRANGE("srate", force_srate, 0, 1000, 8*48000),
- OPT_INTRANGE("channels", audio_output_channels, 0, 1, 8),
+ OPT_CHMAP("channels", audio_output_channels, CONF_MIN, .min = 1),
OPT_AUDIOFORMAT("format", audio_output_format, 0),
OPT_FLOATRANGE("speed", playback_speed, 0, 0.01, 100.0),
@@ -428,15 +424,12 @@ const m_option_t common_opts[] = {
// ignore header-specified delay (dwStart)
OPT_FLAG("ignore-start", ignore_start, 0),
- OPT_FLOATRANGE("a52drc", drc_level, 0, 0, 2),
-
// ------------------------- codec/vfilter options --------------------
// MP3-only: select stereo/left/right
{"stereo", &fakemono, CONF_TYPE_INT, CONF_RANGE, 0, 2, NULL},
{"af*", &af_cfg.list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
- {"af-adv", (void *) audio_filter_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
OPT_SETTINGSLIST("vf*", vf_settings, 0, &vf_obj_list),
@@ -472,6 +465,8 @@ const m_option_t common_opts[] = {
{"lavdopts", (void *) lavc_decode_opts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
{"lavfdopts", (void *) lavfdopts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0, NULL},
+
+ {"ad-lavc", (void *) ad_lavc_decode_opts_conf, CONF_TYPE_SUBCONFIG},
// ------------------------- subtitles options --------------------
OPT_STRINGLIST("sub", sub_name, 0),
diff --git a/core/command.c b/core/command.c
index d9788084db..d8a8882c26 100644
--- a/core/command.c
+++ b/core/command.c
@@ -684,20 +684,10 @@ static int mp_property_channels(m_option_t *prop, int action, void *arg,
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_PRINT:
- switch (mpctx->sh_audio->channels) {
- case 1:
- *(char **) arg = talloc_strdup(NULL, "mono");
- break;
- case 2:
- *(char **) arg = talloc_strdup(NULL, "stereo");
- break;
- default:
- *(char **) arg = talloc_asprintf(NULL, "%d channels",
- mpctx->sh_audio->channels);
- }
+ *(char **) arg = mp_chmap_to_str(&mpctx->sh_audio->channels);
return M_PROPERTY_OK;
case M_PROPERTY_GET:
- *(int *)arg = mpctx->sh_audio->channels;
+ *(int *)arg = mpctx->sh_audio->channels.num;
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
@@ -2319,7 +2309,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
break;
}
af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
- af_reinit(sh_audio->afilter, af);
+ af_reinit(sh_audio->afilter);
}
break;
case MP_CMD_SHOW_CHAPTERS:
diff --git a/core/defaultopts.c b/core/defaultopts.c
index b80bea5c1e..3ffc5e2555 100644
--- a/core/defaultopts.c
+++ b/core/defaultopts.c
@@ -4,6 +4,7 @@
#include "defaultopts.h"
#include "core/options.h"
#include "audio/mixer.h"
+#include "audio/chmap.h"
void set_default_mplayer_options(struct MPOpts *opts)
{
@@ -72,10 +73,9 @@ void set_default_mplayer_options(struct MPOpts *opts)
.audio_display = 1,
.sub_visibility = 1,
.extension_parsing = 1,
- .audio_output_channels = 2,
+ .audio_output_channels = MP_CHMAP_INIT_STEREO,
.audio_output_format = -1, // AF_FORMAT_UNKNOWN
.playback_speed = 1.,
- .drc_level = 1.,
.movie_aspect = -1.,
.sub_auto = 1,
.osd_bar_visible = 1,
@@ -93,6 +93,10 @@ void set_default_mplayer_options(struct MPOpts *opts)
.workaround_bugs = 1, // autodetect
.error_concealment = 3,
},
+ .ad_lavc_param = {
+ .ac3drc = 1.,
+ .downmix = 1,
+ },
.input = {
.key_fifo_size = 7,
.ar_delay = 200,
diff --git a/core/m_option.c b/core/m_option.c
index a667471407..fcf8cc6a67 100644
--- a/core/m_option.c
+++ b/core/m_option.c
@@ -1604,6 +1604,48 @@ const m_option_type_t m_option_type_afmt = {
.copy = copy_opt,
};
+#include "audio/chmap.h"
+
+static int parse_chmap(const m_option_t *opt, struct bstr name,
+ struct bstr param, void *dst)
+{
+ // min>0: at least min channels, min=0: empty ok, min=-1: invalid ok
+ int min_ch = (opt->flags & M_OPT_MIN) ? opt->min : 1;
+
+ if (bstr_equals0(param, "help")) {
+ mp_chmap_print_help(MSGT_CFGPARSER, MSGL_INFO);
+ return M_OPT_EXIT - 1;
+ }
+
+ if (param.len == 0 && min_ch >= 1)
+ return M_OPT_MISSING_PARAM;
+
+ struct mp_chmap res = {0};
+ if (!mp_chmap_from_str(&res, param)) {
+ mp_msg(MSGT_CFGPARSER, MSGL_ERR,
+ "Error parsing channel layout: %.*s\n", BSTR_P(param));
+ return M_OPT_INVALID;
+ }
+
+ if ((min_ch > 0 && !mp_chmap_is_valid(&res)) ||
+ (min_ch >= 0 && mp_chmap_is_empty(&res)))
+ {
+ mp_msg(MSGT_CFGPARSER, MSGL_ERR,
+ "Invalid channel layout: %.*s\n", BSTR_P(param));
+ return M_OPT_INVALID;
+ }
+
+ *(struct mp_chmap *)dst = res;
+
+ return 1;
+}
+
+const m_option_type_t m_option_type_chmap = {
+ .name = "Audio channels or channel map",
+ .size = sizeof(struct mp_chmap *),
+ .parse = parse_chmap,
+ .copy = copy_opt,
+};
static int parse_timestring(struct bstr str, double *time, char endchar)
{
diff --git a/core/m_option.h b/core/m_option.h
index 0026406569..c94b170424 100644
--- a/core/m_option.h
+++ b/core/m_option.h
@@ -25,6 +25,7 @@
#include "config.h"
#include "core/bstr.h"
+#include "audio/chmap.h"
// m_option allows to parse, print and copy data of various types.
@@ -60,6 +61,7 @@ extern const m_option_type_t m_option_type_afmt;
extern const m_option_type_t m_option_type_color;
extern const m_option_type_t m_option_type_geometry;
extern const m_option_type_t m_option_type_size_box;
+extern const m_option_type_t m_option_type_chmap;
// Callback used by m_option_type_print_func options.
typedef int (*m_opt_func_full_t)(const m_option_t *, const char *, const char *);
@@ -194,6 +196,7 @@ union m_option_value {
struct m_color color;
struct m_geometry geometry;
struct m_geometry size_box;
+ struct mp_chmap chmap;
};
////////////////////////////////////////////////////////////////////////////
@@ -568,6 +571,9 @@ int m_option_required_params(const m_option_t *opt);
#define OPT_AUDIOFORMAT(...) \
OPT_GENERAL(int, __VA_ARGS__, .type = &m_option_type_afmt)
+#define OPT_CHMAP(...) \
+ OPT_GENERAL(struct mp_chmap, __VA_ARGS__, .type = &m_option_type_chmap)
+
#define M_CHOICES(choices) \
.priv = (void *)&(const struct m_opt_choice_alternatives[]){ \
diff --git a/core/mplayer.c b/core/mplayer.c
index 89d463daa1..361d3d5466 100644
--- a/core/mplayer.c
+++ b/core/mplayer.c
@@ -330,7 +330,7 @@ static void print_file_properties(struct MPContext *mpctx, const char *filename)
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
"ID_AUDIO_RATE=%d\n", mpctx->sh_audio->samplerate);
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
- "ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels);
+ "ID_AUDIO_NCH=%d\n", mpctx->sh_audio->channels.num);
start_pts = ds_get_next_pts(mpctx->sh_audio->ds);
}
if (video_start_pts != MP_NOPTS_VALUE) {
@@ -1700,6 +1700,12 @@ void reinit_audio_chain(struct MPContext *mpctx)
mpctx->ao = ao_create(opts, mpctx->input);
mpctx->ao->samplerate = opts->force_srate;
mpctx->ao->format = opts->audio_output_format;
+ // Automatic downmix
+ if (mp_chmap_is_stereo(&opts->audio_output_channels) &&
+ !mp_chmap_is_stereo(&mpctx->sh_audio->channels))
+ {
+ mp_chmap_from_channels(&mpctx->ao->channels, 2);
+ }
}
ao = mpctx->ao;
@@ -1716,6 +1722,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
if (!ao->initialized) {
ao->buffersize = opts->ao_buffersize;
ao->encode_lavc_ctx = mpctx->encode_lavc_ctx;
+ mp_chmap_remove_useless_channels(&ao->channels,
+ &opts->audio_output_channels);
ao_init(ao, opts->audio_driver_list);
if (!ao->initialized) {
mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
@@ -1723,12 +1731,10 @@ void reinit_audio_chain(struct MPContext *mpctx)
goto init_error;
}
ao->buffer.start = talloc_new(ao);
- mp_msg(MSGT_CPLAYER, MSGL_INFO,
- "AO: [%s] %dHz %dch %s (%d bytes per sample)\n",
- ao->driver->info->short_name,
- ao->samplerate, ao->channels,
- af_fmt2str_short(ao->format),
- af_fmt2bits(ao->format) / 8);
+ char *s = mp_audio_fmt_to_str(ao->samplerate, &ao->channels, ao->format);
+ mp_msg(MSGT_CPLAYER, MSGL_INFO, "AO: [%s] %s\n",
+ ao->driver->info->short_name, s);
+ talloc_free(s);
mp_msg(MSGT_CPLAYER, MSGL_V, "AO: Description: %s\nAO: Author: %s\n",
ao->driver->info->name, ao->driver->info->author);
if (strlen(ao->driver->info->comment) > 0)
@@ -2295,7 +2301,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize)
ptsdiff = written_pts - mpctx->sh_video->pts - mpctx->delay
- mpctx->audio_delay;
bytes = ptsdiff * bps;
- bytes -= bytes % (ao->channels * af_fmt2bits(ao->format) / 8);
+ bytes -= bytes % (ao->channels.num * af_fmt2bits(ao->format) / 8);
// ogg demuxers give packets without timing
if (written_pts <= 1 && sh_audio->pts == MP_NOPTS_VALUE) {
@@ -2364,7 +2370,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
bool partial_fill = false;
sh_audio_t * const sh_audio = mpctx->sh_audio;
bool modifiable_audio_format = !(ao->format & AF_FORMAT_SPECIAL_MASK);
- int unitsize = ao->channels * af_fmt2bits(ao->format) / 8;
+ int unitsize = ao->channels.num * af_fmt2bits(ao->format) / 8;
if (mpctx->paused)
playsize = 1; // just initialize things (audio pts at least)
diff --git a/core/options.h b/core/options.h
index 9905397f0e..11286bf883 100644
--- a/core/options.h
+++ b/core/options.h
@@ -155,12 +155,11 @@ typedef struct MPOpts {
double force_fps;
- int audio_output_channels;
+ struct mp_chmap audio_output_channels;
int audio_output_format;
int force_srate;
int dtshd;
float playback_speed;
- float drc_level;
struct m_obj_settings *vf_settings;
float movie_aspect;
int flip;
@@ -210,6 +209,12 @@ typedef struct MPOpts {
char *avopt;
} lavc_param;
+ struct ad_lavc_param {
+ float ac3drc;
+ int downmix;
+ char *avopt;
+ } ad_lavc_param;
+
struct lavfdopts {
int probesize;
int probescore;
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index e11d28c043..279f7f7b33 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -340,7 +340,9 @@ static void handle_stream(demuxer_t *demuxer, int i)
sh_audio->format = codec->codec_tag;
// probably unneeded
- sh_audio->channels = codec->channels;
+ mp_chmap_from_channels(&sh_audio->channels, codec->channels);
+ if (codec->channel_layout)
+ mp_chmap_from_lavc(&sh_audio->channels, codec->channel_layout);
sh_audio->samplerate = codec->sample_rate;
sh_audio->i_bps = codec->bit_rate / 8;
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 06db8302b1..62d8dcac7c 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -1349,7 +1349,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
sh_a->format = track->a_formattag;
sh_a->wf->wFormatTag = track->a_formattag;
- sh_a->channels = track->a_channels;
+ mp_chmap_from_channels(&sh_a->channels, track->a_channels);
sh_a->wf->nChannels = track->a_channels;
sh_a->samplerate = (uint32_t) track->a_sfreq;
sh_a->container_out_samplerate = track->a_osfreq;
@@ -1367,7 +1367,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
free(sh_a->wf);
sh_a->wf = NULL;
} else if (track->a_formattag == 0x0001) { /* PCM || PCM_BE */
- sh_a->wf->nAvgBytesPerSec = sh_a->channels * sh_a->samplerate * 2;
+ sh_a->wf->nAvgBytesPerSec = sh_a->channels.num * sh_a->samplerate * 2;
sh_a->wf->nBlockAlign = sh_a->wf->nAvgBytesPerSec;
if (!strcmp(track->codec_id, MKV_A_PCM_BE))
sh_a->format = mmioFOURCC('t', 'w', 'o', 's');
@@ -1539,7 +1539,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
char *data = sh_a->codecdata;
memcpy(data + 0, "TTA1", 4);
AV_WL16(data + 4, 1);
- AV_WL16(data + 6, sh_a->channels);
+ AV_WL16(data + 6, sh_a->channels.num);
AV_WL16(data + 8, sh_a->wf->wBitsPerSample);
AV_WL32(data + 10, sh_a->samplerate);
// Bogus: last frame won't be played.
diff --git a/demux/demux_rawaudio.c b/demux/demux_rawaudio.c
index c6aad60806..3cd2500e03 100644
--- a/demux/demux_rawaudio.c
+++ b/demux/demux_rawaudio.c
@@ -31,12 +31,12 @@
#include "audio/format.h"
-static int channels = 2;
+static struct mp_chmap channels = MP_CHMAP_INIT_STEREO;
static int samplerate = 44100;
static int format = AF_FORMAT_S16_NE;
const m_option_t demux_rawaudio_opts[] = {
- { "channels", &channels, CONF_TYPE_INT,CONF_RANGE,1,8, NULL },
+ { "channels", &channels, &m_option_type_chmap, CONF_MIN, 1 },
{ "rate", &samplerate, CONF_TYPE_INT,CONF_RANGE,1000,8*48000, NULL },
{ "format", &format, CONF_TYPE_AFMT, 0, 0, 0, NULL },
{NULL, NULL, 0, 0, 0, 0, NULL}
@@ -55,11 +55,12 @@ static demuxer_t* demux_rawaudio_open(demuxer_t* demuxer) {
sh_audio->format = format;
sh_audio->wf = w = malloc(sizeof(*w));
w->wFormatTag = 0;
- w->nChannels = sh_audio->channels = channels;
+ sh_audio->channels = channels;
+ w->nChannels = sh_audio->channels.num;
w->nSamplesPerSec = sh_audio->samplerate = samplerate;
int samplesize = (af_fmt2bits(format) + 7) / 8;
- w->nAvgBytesPerSec = samplerate * samplesize * channels;
- w->nBlockAlign = channels * samplesize;
+ w->nAvgBytesPerSec = samplerate * samplesize * w->nChannels;
+ w->nBlockAlign = w->nChannels * samplesize;
w->wBitsPerSample = 8 * samplesize;
w->cbSize = 0;
@@ -105,7 +106,7 @@ static void demux_rawaudio_seek(demuxer_t *demuxer,float rel_seek_secs,float aud
else
pos = base + (rel_seek_secs*sh_audio->i_bps);
- pos -= (pos % (sh_audio->channels * sh_audio->samplesize) );
+ pos -= (pos % (sh_audio->channels.num * sh_audio->samplesize) );
stream_seek(s,pos);
// printf("demux_rawaudio: streamtell=%d\n",(int)stream_tell(demuxer->stream));
}
diff --git a/demux/stheader.h b/demux/stheader.h
index 2bbb74160e..433dc7ef71 100644
--- a/demux/stheader.h
+++ b/demux/stheader.h
@@ -23,6 +23,7 @@
#include "codec_tags.h"
+#include "audio/chmap.h"
#include "aviheader.h"
#include "ms_hdr.h"
struct MPOpts;
@@ -96,8 +97,8 @@ typedef struct sh_audio {
int samplerate;
int container_out_samplerate;
int samplesize;
- int channels;
- int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec)
+ struct mp_chmap channels;
+ int o_bps; // == samplerate*samplesize*channels.num (uncompr. bytes/sec)
int i_bps; // == bitrate (compressed bytes/sec)
// in buffers:
int audio_in_minsize; // initial size to allocate for a_in_buffer if any
diff --git a/stream/tv.c b/stream/tv.c
index 80dd53e6a1..38368d4925 100644
--- a/stream/tv.c
+++ b/stream/tv.c
@@ -792,23 +792,25 @@ static demuxer_t* demux_open_tv(demuxer_t *demuxer)
&sh_audio->samplerate);
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLESIZE,
&sh_audio->samplesize);
+ int nchannels = sh_audio->channels.num;
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS,
- &sh_audio->channels);
+ &nchannels);
+ mp_chmap_from_channels(&sh_audio->channels, nchannels);
sh_audio->gsh->codec = "mp-pcm";
sh_audio->format = audio_format;
sh_audio->i_bps = sh_audio->o_bps =
sh_audio->samplerate * sh_audio->samplesize *
- sh_audio->channels;
+ sh_audio->channels.num;
// emulate WF for win32 codecs:
sh_audio->wf = malloc(sizeof(*sh_audio->wf));
sh_audio->wf->wFormatTag = sh_audio->format;
- sh_audio->wf->nChannels = sh_audio->channels;
+ sh_audio->wf->nChannels = sh_audio->channels.num;
sh_audio->wf->wBitsPerSample = sh_audio->samplesize * 8;
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate;
- sh_audio->wf->nBlockAlign = sh_audio->samplesize * sh_audio->channels;
+ sh_audio->wf->nBlockAlign = sh_audio->samplesize * sh_audio->channels.num;
sh_audio->wf->nAvgBytesPerSec = sh_audio->i_bps;
mp_tmsg(MSGT_DECVIDEO, MSGL_V, " TV audio: %d channels, %d bits, %d Hz\n",