From b01598510f952f291e9f6314352898a6a40532ba Mon Sep 17 00:00:00 2001 From: Andrew Krasavin Date: Tue, 11 Jan 2022 19:23:52 +0300 Subject: [PATCH] ao_sndio: bugfix and small refactoring for #8314 Changes: * fixed hangups in the loop function and in some other cases * refactoring according to @michaelforney's recommendations in #8314 * a few minor and/or cosmetic changes * ability to build ao_sndio using meson --- audio/out/ao_sndio.c | 87 ++++++++++++++++++++++---------------------- meson.build | 8 ++++ meson_options.txt | 1 + wscript | 3 +- 4 files changed, 54 insertions(+), 45 deletions(-) diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c index 4a6f49ee58..6dc1d5ca9e 100644 --- a/audio/out/ao_sndio.c +++ b/audio/out/ao_sndio.c @@ -2,6 +2,7 @@ * Copyright (c) 2008 Alexandre Ratchov * Copyright (c) 2013 Christian Neukirchen * Copyright (c) 2020 Rozhuk Ivan + * Copyright (c) 2021 Andrew Krasavin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -41,7 +42,7 @@ struct priv { }; -static const struct mp_chmap sndio_layouts[MP_NUM_CHANNELS + 1] = { +static const struct mp_chmap sndio_layouts[] = { {0}, /* empty */ {1, {MP_SPEAKER_ID_FL}}, /* mono */ MP_CHMAP2(FL, FR), /* stereo */ @@ -64,11 +65,9 @@ static void uninit(struct ao *ao); static void process_events(struct ao *ao) { struct priv *p = ao->priv; - - if (!p->playing) - return; + int n = sio_pollfd(p->hdl, p->pfd, POLLOUT); - while (poll(p->pfd, n, 0) < 0 && errno == EINTR) {} + while (poll(p->pfd, n, 0) < 0 && errno == EINTR); sio_revents(p->hdl, p->pfd); } @@ -119,21 +118,15 @@ static int init(struct ao *ao) /* Selecting sound format. */ ao->format = af_fmt_from_planar(ao->format); - for (i = 0, ap = af_to_par;; i++, ap++) { - if (i == MP_ARRAY_SIZE(af_to_par)) { - MP_VERBOSE(ao, "unsupported format\n"); - p->par.bits = 16; - p->par.sig = 1; - p->par.le = SIO_LE_NATIVE; - break; - } + + p->par.bits = 16; + p->par.sig = 1; + p->par.le = SIO_LE_NATIVE; + for (i = 0; i < MP_ARRAY_SIZE(af_to_par); i++) { + ap = &af_to_par[i]; if (ap->format == ao->format) { p->par.bits = ap->bits; p->par.sig = ap->sig; - if (ap->bits > 8) - p->par.le = SIO_LE_NATIVE; - if (ap->bits != SIO_BPS(ap->bits)) - p->par.bps = ap->bits / 8; break; } } @@ -148,10 +141,9 @@ static int init(struct ao *ao) goto err_out; p->par.pchan = ao->channels.num; -#ifdef __FreeBSD__ - /* OSS wrapper have bad defaults, overwrite it. */ - p->par.appbufsz = ((p->par.rate * 25) / 1000); /* 25 ms. */ -#endif + p->par.appbufsz = p->par.rate * 250 / 1000; /* 250ms buffer */ + p->par.round = p->par.rate * 10 / 1000; /* 10ms block size */ + if (!sio_setpar(p->hdl, &p->par)) { MP_ERR(ao, "couldn't set params\n"); goto err_out; @@ -182,7 +174,7 @@ static int init(struct ao *ao) p->havevol = sio_onvol(p->hdl, volcb, ao); sio_onmove(p->hdl, movecb, ao); - p->pfd = calloc(sio_nfds(p->hdl), sizeof(struct pollfd)); + p->pfd = talloc_array_ptrtype(p, p->pfd, sio_nfds(p->hdl)); if (!p->pfd) goto err_out; @@ -212,7 +204,6 @@ static void uninit(struct ao *ao) sio_close(p->hdl); p->hdl = NULL; } - free(p->pfd); p->pfd = NULL; p->playing = false; } @@ -243,23 +234,16 @@ static void reset(struct ao *ao) { struct priv *p = ao->priv; - process_events(ao); - p->delay = 0; - p->playing = false; + if (p->playing) { + p->playing = false; - /* XXX: some times may block here then sndiod used. */ - if (!sio_stop(p->hdl)) { - MP_ERR(ao, "reset: couldn't sio_stop()\n"); -reinit: - /* Without this device will never work again. */ - MP_WARN(ao, "Force reinitialize audio device.\n"); - uninit(ao); - init(ao); - return; - } - if (!sio_start(p->hdl)) { - MP_ERR(ao, "reset: sio_start() fail.\n"); - goto reinit; + if (!sio_stop(p->hdl)) { + MP_ERR(ao, "reset: couldn't sio_stop()\n"); + } + p->delay = 0; + if (!sio_start(p->hdl)) { + MP_ERR(ao, "reset: sio_start() fail.\n"); + } } } @@ -286,7 +270,6 @@ static bool audio_write(struct ao *ao, void **data, int samples) return false; } p->delay += samples; - process_events(ao); return true; } @@ -297,10 +280,28 @@ static void get_state(struct ao *ao, struct mp_pcm_state *state) process_events(ao); - state->free_samples = (ao->device_buffer - p->delay); + /* how many samples we can play without blocking */ + state->free_samples = ao->device_buffer - p->delay; + state->free_samples = state->free_samples / p->par.round * p->par.round; + /* how many samples are already in the buffer to be played */ state->queued_samples = p->delay; - state->delay = (p->delay / (double)p->par.rate); - state->playing = p->playing; + /* delay in seconds between first and last sample in buffer */ + state->delay = p->delay / (double)p->par.rate; + + /* report unexpected EOF / underrun */ + if (state->queued_samples && state->queued_samples && + state->queued_samples < state->free_samples && + p->playing || sio_eof(p->hdl)) + { + MP_VERBOSE(ao, "get_state: EOF/underrun detected.\n"); + MP_VERBOSE(ao, "get_state: free: %d, queued: %d, delay: %lf\n", \ + state->free_samples, state->queued_samples, state->delay); + p->playing = false; + state->playing = p->playing; + ao_wakeup_playthread(ao); + } else { + state->playing = p->playing; + } } const struct ao_driver audio_out_sndio = { diff --git a/meson.build b/meson.build index 159ec0bd4a..c9f91f41a5 100644 --- a/meson.build +++ b/meson.build @@ -909,6 +909,13 @@ if sdl2_audio.allowed() sources += files('audio/out/ao_sdl.c') endif +sndio = dependency('sndio', required: get_option('sndio')) +if sndio.found() + dependencies += sndio + features += 'sndio' + sources += files('audio/out/ao_sndio.c') +endif + wasapi = cc.has_header_symbol('audioclient.h', 'IAudioClient', required: get_option('wasapi')) if wasapi features += 'wasapi' @@ -1779,6 +1786,7 @@ conf_data.set10('HAVE_SDL2_GAMEPAD', sdl2_gamepad.allowed()) conf_data.set10('HAVE_SDL2_VIDEO', sdl2_video.allowed()) conf_data.set10('HAVE_SHADERC', shaderc.found()) conf_data.set10('HAVE_SIXEL', sixel.found()) +conf_data.set10('HAVE_SNDIO', sndio.found()) conf_data.set10('HAVE_STDATOMIC', stdatomic.found()) conf_data.set10('HAVE_TA_LEAK_REPORT', get_option('ta-leak-report')) conf_data.set10('HAVE_TESTS', get_option('tests')) diff --git a/meson_options.txt b/meson_options.txt index 8313f5d716..21367817b6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -50,6 +50,7 @@ option('oss-audio', type: 'feature', value: 'auto', description: 'OSSv4 audio ou option('pipewire', type: 'feature', value: 'auto', description: 'PipeWire audio output') option('pulse', type: 'feature', value: 'auto', description: 'PulseAudio audio output') option('sdl2-audio', type: 'feature', value: 'auto', description: 'SDL2 audio output') +option('sndio', type: 'feature', value: 'auto', description: 'sndio audio output') option('wasapi', type: 'feature', value: 'auto', description: 'WASAPI audio output') # video output features diff --git a/wscript b/wscript index 34a1cafe1c..074fe8277e 100644 --- a/wscript +++ b/wscript @@ -441,8 +441,7 @@ audio_output_features = [ }, { 'name': '--sndio', 'desc': 'sndio audio input/output', - 'func': check_statement('sndio.h', - 'struct sio_par par; sio_initpar(&par); const char *s = SIO_DEVANY', lib='sndio'), + 'func': check_pkg_config('sndio'), 'default': 'disable' }, { 'name': '--pulse',