diff options
author | 2013-05-12 21:47:55 +0200 | |
---|---|---|
committer | 2013-05-12 21:47:55 +0200 | |
commit | e6e5a7b221ef2fcdd5a1982d6fdcb627100447d2 (patch) | |
tree | 08b54ef9bb771434fc7fbe9185793503d3ba314c /audio | |
parent | 6a83ef1552de4a1a71da49e45647ce1a4ce64e53 (diff) | |
parent | 48f94311516dc1426644b3e68b2a48c22727e1e7 (diff) |
Merge branch 'audio_changes'
Conflicts:
audio/out/ao_lavc.c
Diffstat (limited to 'audio')
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, |