summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-05-12 21:47:55 +0200
committerwm4 <wm4@nowhere>2013-05-12 21:47:55 +0200
commite6e5a7b221ef2fcdd5a1982d6fdcb627100447d2 (patch)
tree08b54ef9bb771434fc7fbe9185793503d3ba314c /audio
parent6a83ef1552de4a1a71da49e45647ce1a4ce64e53 (diff)
parent48f94311516dc1426644b3e68b2a48c22727e1e7 (diff)
downloadmpv-e6e5a7b221ef2fcdd5a1982d6fdcb627100447d2.tar.bz2
mpv-e6e5a7b221ef2fcdd5a1982d6fdcb627100447d2.tar.xz
Merge branch 'audio_changes'
Conflicts: audio/out/ao_lavc.c
Diffstat (limited to 'audio')
-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
62 files changed, 3143 insertions, 3498 deletions
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,