[MPlayer-dev-eng] [PATCH] Add audio support for sndio API.
Alexandre Ratchov
alex at caoua.org
Thu Dec 19 10:20:27 CET 2013
On Wed, Dec 18, 2013 at 07:09:01PM +0100, Reimar Döffinger wrote:
> On Wed, Dec 18, 2013 at 10:02:20AM +0100, Alexandre Ratchov wrote:
> > + int space;
> > +
> > + /*
> > + * prepare to start; then refill the buffer with the number of bytes
> > + * audio_pause() consumed (this will trigger start)
> > + */
> > + space = par.bufsz * par.pchan * par.bps - delay;
> > + delay = 0;
> > + if (!sio_start(hdl))
> > + mp_msg(MSGT_AO, MSGL_ERR, "ao2: resume: couldn't start\n");
> > + mp_ao_resume_refill(&audio_out_sndio, space);
>
> The delay = 0 doesn't look right.
> If there is still data in the buffers when we resume, that data will
> cause a delay, too, not just the data we refill.
> This looks to me like it should cause permanent A-V desync if pausing
> only for a very short time (well, until you pause long enough to
> completely empty the buffer at least).
You're right; so the "delay" counter must be saved before we call
sio_stop(). See new diff, below.
It works here, no A/V desync. Pausing drains the buffer (I hear
~250ms of audio after I hit space). Resuming, plays the same amount
of silence instead of the original sound. A/V sync seems robust. I
tried to amplify the effect by forcing a huge buffer (1.3s), and
couldn't trigger any desync.
> It should also result in delay potentially becoming negative.
>
This can't happen, it's warranted by sndio internals that the delay
is in the 0..(bufsz-1) range (except during execution of
sio_write(), when it can't be calculated)
> That is assuming that the sio_stop/sio_start sequence doesn't have
> any strange side-effects.
>
Delays caused by drained buffers are the only side effects.
-- Alexandre
Index: Makefile
===================================================================
--- Makefile (revision 36544)
+++ Makefile (working copy)
@@ -572,6 +572,7 @@
libvo/vo_sdl.c \
libvo/sdl_common.c
SRCS_MPLAYER-$(SGIAUDIO) += libao2/ao_sgi.c
+SRCS_MPLAYER-$(SNDIO) += libao2/ao_sndio.c
SRCS_MPLAYER-$(SUNAUDIO) += libao2/ao_sun.c
SRCS_MPLAYER-$(SVGA) += libvo/vo_svga.c
SRCS_MPLAYER-$(TDFXFB) += libvo/vo_tdfxfb.c
Index: configure
===================================================================
--- configure (revision 36544)
+++ configure (working copy)
@@ -522,6 +522,7 @@
--disable-openal disable OpenAL audio output [autodetect]
--disable-nas disable NAS audio output [autodetect]
--disable-sgiaudio disable SGI audio output [autodetect]
+ --disable-sndio disable sndio audio output [autodetect]
--disable-sunaudio disable Sun audio output [autodetect]
--disable-kai disable KAI audio output [autodetect]
--disable-dart disable DART audio output [autodetect]
@@ -755,6 +756,7 @@
_xf86keysym=auto
_mlib=no #broken, thus disabled
_sgiaudio=auto
+_sndio=auto
_sunaudio=auto
_alsa=auto
_fastmemcpy=yes
@@ -1197,6 +1199,8 @@
--disable-sunaudio) _sunaudio=no ;;
--enable-sgiaudio) _sgiaudio=yes ;;
--disable-sgiaudio) _sgiaudio=no ;;
+ --enable-sndio) _sndio=yes ;;
+ --disable-sndio) _sndio=no ;;
--enable-alsa) _alsa=yes ;;
--disable-alsa) _alsa=no ;;
--enable-tv) _tv=yes ;;
@@ -5870,6 +5874,22 @@
fi #if irix
+echocheck "sndio audio"
+if test "$_sndio" = auto ; then
+ _sndio=no
+ statement_check sndio.h 'sio_open(SIO_DEVANY, SIO_PLAY, 0)' -lsndio && _sndio=yes
+fi
+if test "$_sndio" = yes ; then
+ def_sndio='#define CONFIG_SNDIO_AUDIO 1'
+ aomodules="sndio $aomodules"
+ extra_ldflags="$extra_ldflags -lsndio"
+else
+ def_sndio='#undef CONFIG_SNDIO_AUDIO'
+ noaomodules="sndio $noaomodules"
+fi
+echores "$_sndio"
+
+
if os2 ; then
echocheck "KAI (UNIAUD/DART)"
if test "$_kai" = auto; then
@@ -8452,6 +8472,7 @@
SPEEX = $_speex
STREAM_CACHE = $_stream_cache
SGIAUDIO = $_sgiaudio
+SNDIO = $_sndio
SUNAUDIO = $_sunaudio
SVGA = $_svga
TDFXFB = $_tdfxfb
@@ -8888,6 +8909,7 @@
$def_ossaudio_devmixer
$def_pulse
$def_sgiaudio
+$def_sndio
$def_sunaudio
$def_win32waveout
Index: libao2/ao_sndio.c
===================================================================
--- libao2/ao_sndio.c (revision 0)
+++ libao2/ao_sndio.c (working copy)
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2008 Alexandre Ratchov <alex at caoua.org>
+ *
+ * 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 <sys/types.h>
+#include <poll.h>
+#include <errno.h>
+#include <sndio.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "mixer.h"
+#include "help_mp.h"
+
+#include "libaf/af_format.h"
+
+#include "audio_out.h"
+#include "audio_out_internal.h"
+
+static ao_info_t info = {
+ "sndio audio output",
+ "sndio",
+ "Alexandre Ratchov <alex at caoua.org>",
+ ""
+};
+
+LIBAO_EXTERN(sndio)
+
+static struct sio_hdl *hdl = NULL;
+struct pollfd *pfds = NULL;
+static struct sio_par par;
+static int delay, vol, havevol;
+static int prepause_delay;
+
+/*
+ * control misc parameters (only the volume for now)
+ */
+static int control(int cmd, void *arg)
+{
+ ao_control_vol_t *ctl = arg;
+
+ switch (cmd) {
+ case AOCONTROL_GET_VOLUME:
+ if (!havevol)
+ return CONTROL_FALSE;
+ ctl->left = ctl->right = vol * 100 / SIO_MAXVOL;
+ break;
+ case AOCONTROL_SET_VOLUME:
+ if (!havevol)
+ return CONTROL_FALSE;
+ sio_setvol(hdl, ctl->left * SIO_MAXVOL / 100);
+ break;
+ default:
+ return CONTROL_UNKNOWN;
+ }
+ return CONTROL_OK;
+}
+
+/*
+ * call-back invoked whenever the the hardware position changes
+ */
+static void movecb(void *addr, int delta)
+{
+ delay -= delta * (int)(par.bps * par.pchan);
+}
+
+/*
+ * call-back invoked whenever the volume changes
+ */
+static void volcb(void *addr, unsigned newvol)
+{
+ vol = newvol;
+}
+
+/*
+ * open device and setup parameters
+ * return: 1 = success, 0 = failure
+ */
+static int init(int rate, int channels, int format, int flags)
+{
+ int bpf;
+
+ hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
+ if (hdl == NULL) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: can't open sndio\n");
+ return 0;
+ }
+ sio_initpar(&par);
+ par.bits = af_fmt2bits(format);
+ par.sig = (format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI;
+ if (par.bits > 8)
+ par.le = (format & AF_FORMAT_END_MASK) == AF_FORMAT_LE;
+ par.rate = rate;
+ par.pchan = channels;
+ par.appbufsz = par.rate * 250 / 1000; /* 250ms buffer */
+ par.round = par.rate * 10 / 1000; /* 10ms block size */
+ if (!sio_setpar(hdl, &par)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't set params\n");
+ goto bad_close;
+ }
+ if (!sio_getpar(hdl, &par)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't get params\n");
+ goto bad_close;
+ }
+ if (par.bps != SIO_BPS(par.bits)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: unsupported format\n");
+ goto bad_close;
+ }
+ pfds = calloc(sio_nfds(hdl), sizeof(*pfds));
+ if (pfds == NULL) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: couldn't allocate poll fds\n");
+ goto bad_close;
+ }
+ bpf = par.bps * par.pchan;
+ ao_data.format = af_bits2fmt(8 * par.bps);
+ ao_data.format |= par.sig ? AF_FORMAT_SI : AF_FORMAT_US;
+ if (par.bits > 8)
+ ao_data.format |= par.le ? AF_FORMAT_LE : AF_FORMAT_BE;
+ ao_data.channels = par.pchan;
+ ao_data.bps = bpf * par.rate;
+ ao_data.buffersize = par.bufsz * bpf;
+ ao_data.outburst = par.round * bpf;
+ ao_data.samplerate = rate;
+ havevol = sio_onvol(hdl, volcb, NULL);
+ sio_onmove(hdl, movecb, NULL);
+
+ /*
+ * prepare the device to start. It will start as soon there's enough
+ * data in the buffer to not underrun
+ */
+ delay = 0;
+ if (!sio_start(hdl)) {
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: init: couldn't start\n");
+ goto bad_free;
+ }
+ return 1;
+bad_free:
+ free(pfds);
+ pfds = NULL;
+bad_close:
+ sio_close(hdl);
+ hdl = NULL;
+ return 0;
+}
+
+/*
+ * close device
+ */
+static void uninit(int immed)
+{
+ if (hdl)
+ sio_close(hdl);
+ if (pfds)
+ free(pfds);
+}
+
+/*
+ * stop playing and prepare to restart
+ */
+static void reset(void)
+{
+ if (!sio_stop(hdl))
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: reset: couldn't stop\n");
+ delay = 0;
+ if (!sio_start(hdl))
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: reset: couldn't start\n");
+}
+
+/*
+ * refresh the "delay" counter: call sio_revents() which keeps track of
+ * the hardware position
+ */
+static void refresh(void)
+{
+ int n;
+
+ n = sio_pollfd(hdl, pfds, POLLOUT);
+ while (poll(pfds, n, 0) < 0 && errno == EINTR)
+ ; /* nothing */
+ sio_revents(hdl, pfds);
+}
+
+/*
+ * return the number of bytes available in the device buffer
+ */
+static int get_space(void)
+{
+ refresh();
+ return par.bufsz * par.pchan * par.bps - delay;
+}
+
+/*
+ * delay in seconds between first and last sample in the buffer
+ */
+static float get_delay(void)
+{
+ refresh();
+ return (float)delay / (par.bps * par.pchan * par.rate);
+}
+
+/*
+ * submit the given number of bytes to the device
+ */
+static int play(void *data, int len, int flags)
+{
+ int n;
+
+ n = sio_write(hdl, data, len);
+ delay += n;
+ return n;
+}
+
+/*
+ * pause playing
+ */
+static void audio_pause(void)
+{
+ /*
+ * sndio can't pause, so just stop
+ */
+ prepause_delay = delay;
+ if (!sio_stop(hdl))
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: pause: couldn't stop\n");
+ delay = 0;
+}
+
+/*
+ * resume playing after audio_pause()
+ */
+static void audio_resume(void)
+{
+ /*
+ * prepare to start; then refill the buffer with the number of bytes
+ * audio_pause() consumed (this will trigger start)
+ */
+ if (!sio_start(hdl))
+ mp_msg(MSGT_AO, MSGL_ERR, "ao2: resume: couldn't start\n");
+ mp_ao_resume_refill(&audio_out_sndio,
+ par.bufsz * par.pchan * par.bps - prepause_delay);
+}
Index: libao2/audio_out.c
===================================================================
--- libao2/audio_out.c (revision 36544)
+++ libao2/audio_out.c (working copy)
@@ -40,6 +40,7 @@
extern const ao_functions_t audio_out_openal;
extern const ao_functions_t audio_out_null;
extern const ao_functions_t audio_out_alsa;
+extern const ao_functions_t audio_out_sndio;
extern const ao_functions_t audio_out_nas;
extern const ao_functions_t audio_out_sdl;
extern const ao_functions_t audio_out_sun;
@@ -73,6 +74,9 @@
#ifdef CONFIG_COREAUDIO
&audio_out_coreaudio,
#endif
+#ifdef CONFIG_SNDIO_AUDIO
+ &audio_out_sndio,
+#endif
#ifdef CONFIG_OSS_AUDIO
&audio_out_oss,
#endif
More information about the MPlayer-dev-eng
mailing list