summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/audio.h2
-rw-r--r--audio/chmap.c28
-rw-r--r--audio/chmap.h1
-rw-r--r--audio/chmap_sel.c11
-rw-r--r--audio/chmap_sel.h1
-rw-r--r--audio/decode/ad_lavc.c15
-rw-r--r--audio/decode/ad_spdif.c7
-rw-r--r--audio/decode/dec_audio.c21
-rw-r--r--audio/filter/af_lavcac3enc.c37
-rw-r--r--audio/filter/af_scaletempo.c3
-rw-r--r--audio/format.c2
-rw-r--r--audio/out/ao_alsa.c430
-rw-r--r--audio/out/ao_jack.c6
13 files changed, 288 insertions, 276 deletions
diff --git a/audio/audio.h b/audio/audio.h
index bf5358274a..c74d0f778c 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -37,6 +37,8 @@ struct mp_audio {
int num_planes; // number of planes
int bps; // size of sub-samples (af_fmt_to_bytes(format))
+ double pts; // currently invalid within the filter chain
+
// --- private
// These do not necessarily map directly to planes[]. They can have
// different order or count. There shouldn't be more buffers than planes.
diff --git a/audio/chmap.c b/audio/chmap.c
index fb98be52df..e0f485c4dc 100644
--- a/audio/chmap.c
+++ b/audio/chmap.c
@@ -93,7 +93,7 @@ static const char *const std_layout_names[][2] = {
{"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"},
- {"7.1(rear)", "fl-fr-fc-lfe-bl-br-sdl-sdr"},
+ {"7.1(rear)", "fl-fr-fc-lfe-bl-br-sdl-sdr"}, // not in lavc
{"octagonal", "fl-fr-fc-bl-br-bc-sl-sr"},
{"downmix", "dl-dr"},
{"auto", ""}, // not in lavc
@@ -112,19 +112,6 @@ static const struct mp_chmap default_layouts[] = {
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).
-// I'm not sure about the 4 channel case: ALSA uses "quad", while the ffmpeg
-// default layout is "4.0".
-static const char *const mplayer_layouts[MP_NUM_CHANNELS + 1] = {
- [1] = "mono",
- [2] = "stereo",
- [4] = "quad",
- [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)
{
@@ -227,19 +214,6 @@ void mp_chmap_from_channels(struct mp_chmap *dst, int num_channels)
mp_chmap_set_unknown(dst, 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.
diff --git a/audio/chmap.h b/audio/chmap.h
index 77769ec3ff..b32c63b6fc 100644
--- a/audio/chmap.h
+++ b/audio/chmap.h
@@ -108,7 +108,6 @@ void mp_chmap_fill_na(struct mp_chmap *map, int num);
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);
diff --git a/audio/chmap_sel.c b/audio/chmap_sel.c
index 51a3eecf8b..8ddb7131e7 100644
--- a/audio/chmap_sel.c
+++ b/audio/chmap_sel.c
@@ -95,17 +95,6 @@ void mp_chmap_sel_add_waveext(struct mp_chmap_sel *s)
s->allow_waveext = true;
}
-// Classic ALSA-based MPlayer layouts.
-void mp_chmap_sel_add_alsa_def(struct mp_chmap_sel *s)
-{
- for (int n = 1; 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);
- }
-}
-
// Add a channel map that should be allowed.
void mp_chmap_sel_add_map(struct mp_chmap_sel *s, const struct mp_chmap *map)
{
diff --git a/audio/chmap_sel.h b/audio/chmap_sel.h
index 786f06f239..12ded3b466 100644
--- a/audio/chmap_sel.h
+++ b/audio/chmap_sel.h
@@ -37,7 +37,6 @@ struct mp_chmap_sel {
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);
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index ce628877be..3188834830 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -191,12 +191,6 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
- // If we don't have a PTS yet, use the first packet PTS we can get.
- if (da->pts == MP_NOPTS_VALUE && mpkt && mpkt->pts != MP_NOPTS_VALUE) {
- da->pts = mpkt->pts;
- da->pts_offset = 0;
- }
-
int got_frame = 0;
av_frame_unref(priv->avframe);
int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
@@ -225,10 +219,6 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
return mpkt ? AD_OK : AD_EOF;
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
- if (out_pts != MP_NOPTS_VALUE) {
- da->pts = out_pts;
- da->pts_offset = 0;
- }
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
@@ -244,6 +234,8 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
}
mp_audio_set_channels(mpframe, &lavc_chmap);
+ mpframe->pts = out_pts;
+
#if HAVE_AVFRAME_SKIP_SAMPLES
AVFrameSideData *sd =
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
@@ -254,7 +246,8 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
if (skip) {
mp_audio_skip_samples(mpframe, skip);
- da->pts_offset += skip;
+ if (mpframe->pts != MP_NOPTS_VALUE)
+ mpframe->pts += skip / (double)mpframe->rate;
priv->skip_samples -= skip;
}
if (pad <= mpframe->samples)
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c
index 88f3f2ba2b..5e9dcf1c4f 100644
--- a/audio/decode/ad_spdif.c
+++ b/audio/decode/ad_spdif.c
@@ -253,13 +253,11 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
if (!mpkt)
return AD_EOF;
+ double pts = mpkt->pts;
+
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
pkt.pts = pkt.dts = 0;
- if (mpkt->pts != MP_NOPTS_VALUE) {
- da->pts = mpkt->pts;
- da->pts_offset = 0;
- }
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da, &pkt) < 0)
return AD_ERR;
@@ -276,6 +274,7 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
return AD_ERR;
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
+ (*out)->pts = pts;
return 0;
}
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index edf26951f7..03172ed294 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <math.h>
#include <assert.h>
#include <libavutil/mem.h>
@@ -170,14 +171,28 @@ static int decode_new_frame(struct dec_audio *da)
if (ret < 0)
return ret;
- if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
- da->pts = 0;
-
if (da->waiting) {
+ if (da->waiting->pts != MP_NOPTS_VALUE) {
+ if (da->pts != MP_NOPTS_VALUE) {
+ da->pts += da->pts_offset / (double)da->waiting->rate;
+ da->pts_offset = 0;
+ }
+ // Keep the interpolated timestamp if it doesn't deviate more
+ // than 1 ms from the real one. (MKV rounded timestamps.)
+ if (da->pts == MP_NOPTS_VALUE || da->pts_offset != 0 ||
+ fabs(da->pts - da->waiting->pts) > 0.001)
+ {
+ da->pts = da->waiting->pts;
+ da->pts_offset = 0;
+ }
+ }
da->pts_offset += da->waiting->samples;
da->decode_format = *da->waiting;
mp_audio_set_null_data(&da->decode_format);
}
+
+ if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
+ da->pts = 0;
}
return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR;
}
diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c
index c1e6b964ad..973e688aaa 100644
--- a/audio/filter/af_lavcac3enc.c
+++ b/audio/filter/af_lavcac3enc.c
@@ -49,7 +49,6 @@ const uint16_t ac3_bitrate_tab[19] = {
typedef struct af_ac3enc_s {
struct AVCodec *lavc_acodec;
struct AVCodecContext *lavc_actx;
- AVPacket pkt;
int bit_rate;
struct mp_audio *input; // frame passed to libavcodec
struct mp_audio *pending; // unconsumed input data
@@ -146,7 +145,6 @@ static void uninit(struct af_instance* af)
af_ac3enc_t *s = af->priv;
if (s) {
- av_packet_unref(&s->pkt);
if(s->lavc_actx) {
avcodec_close(s->lavc_actx);
av_free(s->lavc_actx);
@@ -212,6 +210,8 @@ static int filter_out(struct af_instance *af)
MP_FATAL(af, "Could not allocate memory \n");
return -1;
}
+ int err = -1;
+
frame->nb_samples = s->in_samples;
frame->format = s->lavc_actx->sample_fmt;
frame->channel_layout = s->lavc_actx->channel_layout;
@@ -221,30 +221,33 @@ static int filter_out(struct af_instance *af)
frame->data[n] = s->input->planes[n];
frame->linesize[0] = s->input->samples * s->input->sstride;
+ AVPacket pkt = {0};
+ av_init_packet(&pkt);
+
int ok;
- int lavc_ret = avcodec_encode_audio2(s->lavc_actx, &s->pkt, frame, &ok);
+ int lavc_ret = avcodec_encode_audio2(s->lavc_actx, &pkt, frame, &ok);
av_frame_free(&frame);
s->input->samples = 0;
if (lavc_ret < 0 || !ok) {
MP_FATAL(af, "Encode failed.\n");
- return -1;
+ goto done;
}
MP_DBG(af, "avcodec_encode_audio got %d, pending %d.\n",
- s->pkt.size, s->pending->samples);
+ pkt.size, s->pending->samples);
struct mp_audio *out =
mp_audio_pool_get(af->out_pool, af->data, s->out_samples);
if (!out)
- return -1;
+ goto done;
mp_audio_copy_attributes(out, s->pending);
- int frame_size = s->pkt.size;
+ int frame_size = pkt.size;
int header_len = 0;
char hdr[8];
- if (s->cfg_add_iec61937_header && s->pkt.size > 5) {
- int bsmod = s->pkt.data[5] & 0x7;
+ if (s->cfg_add_iec61937_header && pkt.size > 5) {
+ int bsmod = pkt.data[5] & 0x7;
int len = frame_size;
frame_size = AC3_FRAME_SIZE * 2 * 2;
@@ -262,14 +265,18 @@ static int filter_out(struct af_instance *af)
char *buf = (char *)out->planes[0];
memcpy(buf, hdr, header_len);
- memcpy(buf + header_len, s->pkt.data, s->pkt.size);
- memset(buf + header_len + s->pkt.size, 0,
- frame_size - (header_len + s->pkt.size));
- swap_16((uint16_t *)(buf + header_len), s->pkt.size / 2);
+ memcpy(buf + header_len, pkt.data, pkt.size);
+ memset(buf + header_len + pkt.size, 0,
+ frame_size - (header_len + pkt.size));
+ swap_16((uint16_t *)(buf + header_len), pkt.size / 2);
out->samples = frame_size / out->sstride;
af_add_output_frame(af, out);
update_delay(af);
- return 0;
+
+ err = 0;
+done:
+ av_packet_unref(&pkt);
+ return err;
}
static int af_open(struct af_instance* af){
@@ -307,8 +314,6 @@ static int af_open(struct af_instance* af){
MP_VERBOSE(af, "[af_lavcac3enc]: in sample format: %s\n",
af_fmt_to_str(s->in_sampleformat));
- av_init_packet(&s->pkt);
-
s->input = talloc_zero(s, struct mp_audio);
if (s->cfg_bit_rate) {
diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c
index 131dc5d511..4f0eb13091 100644
--- a/audio/filter/af_scaletempo.c
+++ b/audio/filter/af_scaletempo.c
@@ -286,9 +286,6 @@ static void update_speed(struct af_instance *af, float speed)
s->frames_stride_scaled = s->scale * s->frames_stride;
s->frames_stride_error = MPMIN(s->frames_stride_error, s->frames_stride_scaled);
-
- MP_VERBOSE(af, "%.3f speed * %.3f scale_nominal = %.3f\n",
- s->speed, s->scale_nominal, s->scale);
}
// Initialization and runtime control
diff --git a/audio/format.c b/audio/format.c
index 100a0f3225..06360b884b 100644
--- a/audio/format.c
+++ b/audio/format.c
@@ -51,7 +51,7 @@ int af_fmt_change_bytes(int format, int bytes)
if (af_fmt_to_bytes(fmt) == bytes &&
af_fmt_is_float(fmt) == af_fmt_is_float(format) &&
af_fmt_is_planar(fmt) == af_fmt_is_planar(format) &&
- af_fmt_is_spdif(fmt) == af_fmt_is_spdif(format))
+ (fmt == format || (!af_fmt_is_spdif(fmt) && !af_fmt_is_spdif(format))))
return fmt;
}
return 0;
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index 2b3e8e0658..4112ab3c83 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -52,11 +52,11 @@ struct priv {
snd_pcm_t *alsa;
bool device_lost;
snd_pcm_format_t alsa_fmt;
- int can_pause;
+ bool can_pause;
snd_pcm_sframes_t prepause_frames;
double delay_before_pause;
- int buffersize; // in frames
- int outburst; // in frames
+ snd_pcm_uframes_t buffersize;
+ snd_pcm_uframes_t outburst;
char *cfg_device;
char *cfg_mixer_device;
@@ -227,24 +227,19 @@ static const int mp_to_alsa_format[][2] = {
MP_SELECT_LE_BE(SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE)},
{AF_FORMAT_FLOAT, SND_PCM_FORMAT_FLOAT},
{AF_FORMAT_DOUBLE, SND_PCM_FORMAT_FLOAT64},
+ {AF_FORMAT_S_MP3, SND_PCM_FORMAT_MPEG},
{AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN},
};
static int find_alsa_format(int af_format)
{
- if (af_fmt_is_spdif(af_format)) {
- if (af_format == AF_FORMAT_S_MP3) {
- return SND_PCM_FORMAT_MPEG;
- } else {
- return SND_PCM_FORMAT_S16;
- }
- }
-
af_format = af_fmt_from_planar(af_format);
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];
}
+ if (af_fmt_is_spdif(af_format))
+ return SND_PCM_FORMAT_S16;
return SND_PCM_FORMAT_UNKNOWN;
}
@@ -275,6 +270,7 @@ static const int alsa_to_mp_channels[][2] = {
{SND_CHMAP_RLC, MP_SP(SDL)},
{SND_CHMAP_MONO, MP_SP(FC)},
{SND_CHMAP_NA, MP_SPEAKER_ID_NA},
+ {SND_CHMAP_UNKNOWN, MP_SPEAKER_ID_NA},
{SND_CHMAP_LAST, MP_SPEAKER_ID_COUNT}
};
@@ -288,28 +284,48 @@ static int find_mp_channel(int alsa_channel)
return MP_SPEAKER_ID_COUNT;
}
-static int find_alsa_channel(int mp_channel)
+#define CHMAP(n, ...) &(struct mp_chmap) MP_CONCAT(MP_CHMAP, n) (__VA_ARGS__)
+
+// Replace each channel in a with b (a->num == b->num)
+static void replace_submap(struct mp_chmap *dst, struct mp_chmap *a,
+ struct mp_chmap *b)
{
- for (int i = 0; alsa_to_mp_channels[i][1] != MP_SPEAKER_ID_COUNT; i++) {
- if (alsa_to_mp_channels[i][1] == mp_channel)
- return alsa_to_mp_channels[i][0];
+ struct mp_chmap t = *dst;
+ if (!mp_chmap_is_valid(&t) || mp_chmap_diffn(a, &t) != 0)
+ return;
+ assert(a->num == b->num);
+ for (int n = 0; n < t.num; n++) {
+ for (int i = 0; i < a->num; i++) {
+ if (t.speaker[n] == a->speaker[i]) {
+ t.speaker[n] = b->speaker[i];
+ break;
+ }
+ }
}
-
- return SND_CHMAP_UNKNOWN;
+ if (mp_chmap_is_valid(&t))
+ *dst = t;
}
-static int mp_chmap_from_alsa(struct mp_chmap *dst, snd_pcm_chmap_t *src)
+static bool mp_chmap_from_alsa(struct mp_chmap *dst, snd_pcm_chmap_t *src)
{
*dst = (struct mp_chmap) {0};
if (src->channels > MP_NUM_CHANNELS)
- return -1;
+ return false;
dst->num = src->channels;
for (int c = 0; c < dst->num; c++)
dst->speaker[c] = find_mp_channel(src->pos[c]);
- return 0;
+ // Assume anything with 1 channel is mono.
+ if (dst->num == 1)
+ dst->speaker[0] = MP_SP(FC);
+
+ // Remap weird Intel HDA HDMI 7.1 layouts correctly.
+ replace_submap(dst, CHMAP(6, FL, FR, BL, BR, SDL, SDR),
+ CHMAP(6, FL, FR, SL, SR, BL, BR));
+
+ return mp_chmap_is_valid(dst);
}
static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
@@ -322,20 +338,21 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
return false;
for (int i = 0; maps[i] != NULL; i++) {
- struct mp_chmap entry;
- mp_chmap_from_alsa(&entry, &maps[i]->map);
+ char aname[128];
+ if (snd_pcm_chmap_print(&maps[i]->map, sizeof(aname), aname) <= 0)
+ aname[0] = '\0';
- if (mp_chmap_is_valid(&entry)) {
+ struct mp_chmap entry;
+ if (mp_chmap_from_alsa(&entry, &maps[i]->map)) {
+ struct mp_chmap reorder = entry;
if (maps[i]->type == SND_CHMAP_TYPE_VAR)
- mp_chmap_reorder_norm(&entry);
- MP_DBG(ao, "Got supported channel map: %s (type %s)\n",
- mp_chmap_to_str(&entry),
- snd_pcm_chmap_type_name(maps[i]->type));
- mp_chmap_sel_add_map(&chmap_sel, &entry);
+ mp_chmap_reorder_norm(&reorder);
+ MP_DBG(ao, "Got supported channel map: %s (type %s) -> %s -> %s\n",
+ aname, snd_pcm_chmap_type_name(maps[i]->type),
+ mp_chmap_to_str(&entry), mp_chmap_to_str(&reorder));
+ mp_chmap_sel_add_map(&chmap_sel, &reorder);
} else {
- char tmp[128];
- if (snd_pcm_chmap_print(&maps[i]->map, sizeof(tmp), tmp) > 0)
- MP_VERBOSE(ao, "skipping unknown ALSA channel map: %s\n", tmp);
+ MP_VERBOSE(ao, "skipping unknown ALSA channel map: %s\n", aname);
}
}
@@ -344,6 +361,108 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
return ao_chmap_sel_adjust(ao, &chmap_sel, chmap);
}
+// Map back our selected channel layout to an ALSA one. This is done this way so
+// that our ALSA->mp_chmap mapping function only has to go one way.
+// The return value is to be freed with free().
+static snd_pcm_chmap_t *map_back_chmap(struct ao *ao, struct mp_chmap *chmap)
+{
+ struct priv *p = ao->priv;
+ if (!mp_chmap_is_valid(chmap))
+ return NULL;
+
+ snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps(p->alsa);
+ if (!maps)
+ return NULL;
+
+ snd_pcm_chmap_t *alsa_chmap = NULL;
+
+ for (int i = 0; maps[i] != NULL; i++) {
+ struct mp_chmap entry;
+ if (!mp_chmap_from_alsa(&entry, &maps[i]->map))
+ continue;
+
+ if (mp_chmap_equals(chmap, &entry) ||
+ (mp_chmap_equals_reordered(chmap, &entry) &&
+ maps[i]->type == SND_CHMAP_TYPE_VAR))
+ {
+ alsa_chmap = calloc(1, sizeof(*alsa_chmap) +
+ sizeof(alsa_chmap->pos[0]) * entry.num);
+ if (!alsa_chmap)
+ break;
+ alsa_chmap->channels = entry.num;
+
+ // Undo if mp_chmap_reorder() was called on the result.
+ int reorder[MP_NUM_CHANNELS];
+ mp_chmap_get_reorder(reorder, chmap, &entry);
+ for (int n = 0; n < entry.num; n++)
+ alsa_chmap->pos[n] = maps[i]->map.pos[reorder[n]];
+ break;
+ }
+ }
+
+ snd_pcm_free_chmaps(maps);
+ return alsa_chmap;
+}
+
+
+static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels)
+{
+ struct priv *p = ao->priv;
+ int err;
+
+ snd_pcm_chmap_t *alsa_chmap = map_back_chmap(ao, dev_chmap);
+ if (alsa_chmap) {
+ char tmp[128];
+ if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
+ MP_VERBOSE(ao, "trying to set ALSA channel map: %s\n", tmp);
+
+ err = snd_pcm_set_chmap(p->alsa, alsa_chmap);
+ if (err == -ENXIO) {
+ // A device my not be able to set any channel map, even channel maps
+ // that were reported as supported. This is either because the ALSA
+ // device is broken (dmix), or because the driver has only 1
+ // channel map per channel count, and setting the map is not needed.
+ MP_VERBOSE(ao, "device returned ENXIO when setting channel map %s\n",
+ mp_chmap_to_str(dev_chmap));
+ } else {
+ CHECK_ALSA_WARN("Channel map setup failed");
+ }
+
+ free(alsa_chmap);
+ }
+
+ alsa_chmap = snd_pcm_get_chmap(p->alsa);
+ if (alsa_chmap) {
+ char tmp[128];
+ if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
+ MP_VERBOSE(ao, "channel map reported by ALSA: %s\n", tmp);
+
+ struct mp_chmap chmap;
+ mp_chmap_from_alsa(&chmap, alsa_chmap);
+
+ MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
+
+ if (p->cfg_ignore_chmap) {
+ MP_VERBOSE(ao, "user set ignore-chmap; ignoring the channel map.\n");
+ } else if (af_fmt_is_spdif(ao->format)) {
+ MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
+ } else if (!mp_chmap_is_valid(&chmap)) {
+ MP_WARN(ao, "Got unknown channel map from ALSA.\n");
+ } else if (chmap.num != num_channels) {
+ MP_WARN(ao, "ALSA channel map conflicts with channel count!\n");
+ } else {
+ MP_VERBOSE(ao, "using the ALSA channel map.\n");
+ if (mp_chmap_equals(&chmap, &ao->channels))
+ MP_VERBOSE(ao, "which is what we requested.\n");
+ ao->channels = chmap;
+ }
+
+ free(alsa_chmap);
+ }
+
+ return 0;
+}
+
#else /* HAVE_CHMAP_API */
static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
@@ -351,6 +470,11 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
return false;
}
+static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels)
+{
+ return 0;
+}
+
#endif /* else HAVE_CHMAP_API */
static int map_iec958_srate(int srate)
@@ -371,8 +495,8 @@ static int map_iec958_srate(int srate)
}
// ALSA device strings can have parameters. They are usually appended to the
-// device name. Since there can be various forms, and we (sometimes) want to
-// append them to unknown device strings, which possibly already include params.
+// device name. There can be various forms, and we (sometimes) want to append
+// them to unknown device strings, which possibly already include params.
static char *append_params(void *ta_parent, const char *device, const char *p)
{
if (!p || !p[0])
@@ -443,16 +567,14 @@ static void uninit(struct ao *ao)
int err;
err = snd_pcm_close(p->alsa);
+ p->alsa = NULL;
CHECK_ALSA_ERROR("pcm close error");
}
alsa_error: ;
}
-#define INIT_OK 0
-#define INIT_ERROR -1
-#define INIT_BRAINDEATH -2
-static int init_device(struct ao *ao, bool second_try)
+static int init_device(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
@@ -470,18 +592,35 @@ static int init_device(struct ao *ao, bool second_try)
CHECK_ALSA_WARN("Unable to set blocking mode");
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);
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
CHECK_ALSA_ERROR("Unable to get initial parameters");
+ // Some ALSA drivers have broken delay reporting, so disable the ALSA
+ // resampling plugin by default.
+ if (!p->cfg_resample) {
+ err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
+ CHECK_ALSA_ERROR("Unable to disable resampling");
+ }
+
+ snd_pcm_access_t access = af_fmt_is_planar(ao->format)
+ ? SND_PCM_ACCESS_RW_NONINTERLEAVED
+ : SND_PCM_ACCESS_RW_INTERLEAVED;
+ err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
+ if (err < 0 && af_fmt_is_planar(ao->format)) {
+ ao->format = af_fmt_from_planar(ao->format);
+ access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
+ }
+ CHECK_ALSA_ERROR("Unable to set access type");
+
bool found_format = false;
int try_formats[AF_FORMAT_COUNT];
af_get_best_sample_formats(ao->format, try_formats);
for (int n = 0; try_formats[n]; n++) {
+ if (af_fmt_is_planar(ao->format) != af_fmt_is_planar(try_formats[n]))
+ continue; // implied SND_PCM_ACCESS mismatches
p->alsa_fmt = find_alsa_format(try_formats[n]);
MP_VERBOSE(ao, "trying format %s\n", af_fmt_to_str(try_formats[n]));
if (snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt) >= 0) {
@@ -499,17 +638,6 @@ static int init_device(struct ao *ao, bool second_try)
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
CHECK_ALSA_ERROR("Unable to set format");
- snd_pcm_access_t access = af_fmt_is_planar(ao->format)
- ? SND_PCM_ACCESS_RW_NONINTERLEAVED
- : SND_PCM_ACCESS_RW_INTERLEAVED;
- err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
- if (err < 0 && af_fmt_is_planar(ao->format)) {
- ao->format = af_fmt_from_planar(ao->format);
- access = SND_PCM_ACCESS_RW_INTERLEAVED;
- err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
- }
- CHECK_ALSA_ERROR("Unable to set access type");
-
struct mp_chmap dev_chmap = ao->channels;
if (af_fmt_is_spdif(ao->format) || p->cfg_ignore_chmap) {
dev_chmap.num = 0; // disable chmap API
@@ -534,157 +662,51 @@ static int init_device(struct ao *ao, bool second_try)
goto alsa_error;
}
- if (num_channels != ao->channels.num) {
- int req = ao->channels.num;
- mp_chmap_from_channels_alsa(&ao->channels, num_channels);
- if (!mp_chmap_is_valid(&ao->channels))
- mp_chmap_from_channels(&ao->channels, 2);
- MP_ERR(ao, "Asked for %d channels, got %d - fallback to %s.\n", req,
- num_channels, mp_chmap_to_str(&ao->channels));
- }
-
- // Some ALSA drivers have broken delay reporting, so disable the ALSA
- // resampling plugin by default.
- if (!p->cfg_resample) {
- 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");
+ snd_pcm_hw_params_t *hwparams_backup;
+ snd_pcm_hw_params_alloca(&hwparams_backup);
+ snd_pcm_hw_params_copy(hwparams_backup, alsa_hwparams);
+
+ // Cargo-culted buffer settings; might still be useful for PulseAudio.
err = snd_pcm_hw_params_set_buffer_time_near
(p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL);
CHECK_ALSA_WARN("Unable to set buffer time near");
-
- err = snd_pcm_hw_params_set_periods_near
- (p->alsa, alsa_hwparams, &(unsigned int){FRAGCOUNT}, NULL);
- CHECK_ALSA_WARN("Unable to set periods");
+ if (err >= 0) {
+ err = snd_pcm_hw_params_set_periods_near
+ (p->alsa, alsa_hwparams, &(unsigned int){FRAGCOUNT}, NULL);
+ CHECK_ALSA_WARN("Unable to set periods");
+ }
+ if (err < 0)
+ snd_pcm_hw_params_copy(alsa_hwparams, hwparams_backup);
/* 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 */
-
-#if HAVE_CHMAP_API
- if (mp_chmap_is_valid(&dev_chmap)) {
- snd_pcm_chmap_t *alsa_chmap =
- calloc(1, sizeof(*alsa_chmap) +
- sizeof(alsa_chmap->pos[0]) * dev_chmap.num);
- if (!alsa_chmap)
- goto alsa_error;
-
- alsa_chmap->channels = dev_chmap.num;
- for (int c = 0; c < dev_chmap.num; c++)
- alsa_chmap->pos[c] = find_alsa_channel(dev_chmap.speaker[c]);
-
- // mpv and ALSA use different conventions for mono
- if (dev_chmap.num == 1 && dev_chmap.speaker[0] == MP_SP(FC))
- alsa_chmap->pos[0] = SND_CHMAP_MONO;
-
- char tmp[128];
- if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
- MP_VERBOSE(ao, "trying to set ALSA channel map: %s\n", tmp);
-
- err = snd_pcm_set_chmap(p->alsa, alsa_chmap);
- if (err == -ENXIO) {
- // A device my not be able to set any channel map, even channel maps
- // that were reported as supported. This is either because the ALSA
- // device is broken (dmix), or because the driver has only 1
- // channel map per channel count, and setting the map is not needed.
- MP_VERBOSE(ao, "device returned ENXIO when setting channel map %s\n",
- mp_chmap_to_str(&dev_chmap));
- } else {
- CHECK_ALSA_WARN("Channel map setup failed");
- }
-
- free(alsa_chmap);
- }
-
- snd_pcm_chmap_t *alsa_chmap = snd_pcm_get_chmap(p->alsa);
- if (alsa_chmap) {
- char tmp[128];
- if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
- MP_VERBOSE(ao, "channel map reported by ALSA: %s\n", tmp);
-
- struct mp_chmap chmap;
- mp_chmap_from_alsa(&chmap, alsa_chmap);
-
- MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
-
- if (p->cfg_ignore_chmap) {
- MP_VERBOSE(ao, "user set ignore-chmap; ignoring the channel map.\n");
- } else if (af_fmt_is_spdif(ao->format)) {
- MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
- } else if (mp_chmap_is_valid(&chmap)) {
- // Is it one that contains NA channels?
- struct mp_chmap without_na = chmap;
- mp_chmap_remove_na(&without_na);
-
- if (mp_chmap_is_valid(&without_na) &&
- !mp_chmap_equals(&without_na, &chmap) &&
- !mp_chmap_equals(&chmap, &ao->channels) &&
- !second_try)
- {
- // Sometimes, ALSA will advertise certain chmaps, but it's not
- // possible to set them. This can happen with dmix: as of
- // alsa 1.0.28, dmix can do stereo only, but advertises the
- // surround chmaps of the underlying device. In this case,
- // e.g. setting 6 channels will succeed, but requesting 5.1
- // afterwards will fail. Then it will return something like
- // "FL FR NA NA NA NA" as channel map. This means we would
- // have to pad stereo output to 6 channels with silence, which
- // would require lots of extra processing. You can't change
- // the number of channels to 2 either, because the hw params
- // are already set! So just fuck it and reopen the device with
- // the chmap "cleaned out" of NA entries.
- MP_VERBOSE(ao, "Working around braindead ALSA behavior.\n");
- err = snd_pcm_close(p->alsa);
- p->alsa = NULL;
- CHECK_ALSA_ERROR("pcm close error");
- ao->channels = without_na;
- return INIT_BRAINDEATH;
- }
-
- if (mp_chmap_equals(&chmap, &ao->channels)) {
- MP_VERBOSE(ao, "which is what we requested.\n");
- } else if (chmap.num == ao->channels.num) {
- MP_VERBOSE(ao, "using the ALSA channel map.\n");
- ao->channels = chmap;
-