ports/multimedia/kodi/files/extra-patch-sndio
Tobias Kortkamp ad47dafe60 multimedia/kodi: Add sndio backend
This is essentially a backport of [1] which was already merged and
will hopefully be part of the next Kodi release.

[1] https://github.com/xbmc/xbmc/pull/11962

PR:		220231
Approved by:	mickael.maillot@gmail.com (maintainer timeout, 3 months)
2017-10-13 17:42:53 +00:00

542 lines
14 KiB
Text

diff --git configure.ac configure.ac
index 47eb1d9e33..c61564bb0e 100644
--- configure.ac
+++ configure.ac
@@ -315,6 +315,12 @@ AC_ARG_ENABLE([pulse],
[use_pulse=$enableval],
[use_pulse=auto])
+AC_ARG_ENABLE([sndio],
+ [AS_HELP_STRING([--enable-sndio],
+ [enable sndio support (default is auto)])],
+ [use_sndio=$enableval],
+ [use_sndio=auto])
+
AC_ARG_ENABLE([ssh],
[AS_HELP_STRING([--disable-ssh],
[disable SSH SFTP support (default is enabled)])],
@@ -1210,6 +1216,22 @@ else
USE_PULSE=0
fi
+# sndio
+if test "x$use_sndio" != "xno"; then
+ USE_SNDIO=0
+ AC_CHECK_HEADER(sndio.h, HAVE_SNDIO="yes", HAVE_SNDIO="no")
+ if test "x$HAVE_SNDIO" = "xyes"; then
+ AC_CHECK_LIB(sndio, sio_open, HAVE_SNDIO="yes", HAVE_SNDIO="no", [])
+ if test "x$HAVE_SNDIO" = "xyes"; then
+ LIBS="$LIBS -lsndio"
+ USE_SNDIO=1
+ AC_DEFINE([HAVE_SNDIO],[1],[sndio enabled])
+ fi
+ fi
+else
+ USE_SNDIO=0
+fi
+
# avahi
if test "$use_avahi" = "yes"; then
AC_CHECK_LIB([avahi-common], [main],,
@@ -1924,6 +1946,12 @@ else
final_message="$final_message\n PulseAudio:\tNo"
fi
+if test "$use_sndio" = "yes"; then
+ final_message="$final_message\n Sndio Support:\tYes"
+else
+ final_message="$final_message\n Sndio Support:\tNo"
+fi
+
# Google Test Framework
if test "$configure_gtest" = "yes"; then
AC_MSG_NOTICE($gtest_enabled)
@@ -2247,6 +2275,8 @@ AC_SUBST(USE_AIRPLAY)
AC_SUBST(USE_OPENMAX)
AC_SUBST(USE_PULSE)
AC_SUBST(HAVE_LIBPULSE)
+AC_SUBST(USE_SNDIO)
+AC_SUBST(HAVE_SNDIO)
AC_SUBST(USE_ALSA)
AC_SUBST(USE_TEXTUREPACKER)
AC_SUBST(TEXTUREPACKER)
diff --git xbmc/cores/AudioEngine/AESinkFactory.cpp xbmc/cores/AudioEngine/AESinkFactory.cpp
index d7b05cdbfe..617b5dba83 100644
--- xbmc/cores/AudioEngine/AESinkFactory.cpp
+++ xbmc/cores/AudioEngine/AESinkFactory.cpp
@@ -36,6 +36,9 @@
#if defined(HAS_ALSA)
#include "Sinks/AESinkALSA.h"
#endif
+ #if defined(HAS_SNDIO)
+ #include "Sinks/AESinkSNDIO.h"
+ #endif
#if defined(TARGET_FREEBSD)
#include "Sinks/AESinkOSS.h"
#endif
@@ -77,6 +80,9 @@
#if defined(HAS_ALSA)
driver == "ALSA" ||
#endif
+ #if defined(HAS_SNDIO)
+ driver == "SNDIO" ||
+ #endif
#if defined(TARGET_FREEBSD)
driver == "OSS" ||
#endif
@@ -116,11 +122,15 @@ IAESink *CAESinkFactory::TrySink(std::string &driver, std::string &device, AEAud
sink = new CAESinkDARWINIOS();
#elif defined(TARGET_DARWIN_OSX)
sink = new CAESinkDARWINOSX();
-#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
+#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) || defined(TARGET_OPENBSD)
#if defined(HAS_PULSEAUDIO)
if (driver == "PULSE")
sink = new CAESinkPULSE();
#endif
+ #if defined(HAS_SNDIO)
+ if (driver == "SNDIO")
+ sink = new CAESinkSNDIO();
+ #endif
#if defined(HAS_ALSA)
if (driver == "ALSA")
sink = new CAESinkALSA();
@@ -236,6 +246,10 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force)
if (envSink == "PULSE")
CAESinkPULSE::EnumerateDevicesEx(info.m_deviceInfoList, force);
#endif
+ #if defined(HAS_SNDIO)
+ if (envSink == "SNDIO")
+ CAESinkSNDIO::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ #endif
#if defined(HAS_ALSA)
if (envSink == "ALSA")
CAESinkALSA::EnumerateDevicesEx(info.m_deviceInfoList, force);
@@ -264,6 +278,17 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force)
}
#endif
+ #if defined(HAS_SNDIO)
+ info.m_deviceInfoList.clear();
+ info.m_sinkName = "SNDIO";
+ CAESinkSNDIO::EnumerateDevicesEx(info.m_deviceInfoList, force);
+ if(!info.m_deviceInfoList.empty())
+ {
+ list.push_back(info);
+ return;
+ }
+ #endif
+
#if defined(HAS_ALSA)
info.m_deviceInfoList.clear();
info.m_sinkName = "ALSA";
diff --git xbmc/cores/AudioEngine/Makefile.in xbmc/cores/AudioEngine/Makefile.in
index 7aab111f81..788786e2d1 100644
--- xbmc/cores/AudioEngine/Makefile.in
+++ xbmc/cores/AudioEngine/Makefile.in
@@ -55,6 +55,9 @@ SRCS += Sinks/AESinkOSS.cpp
ifeq (@USE_PULSE@,1)
SRCS += Sinks/AESinkPULSE.cpp
endif
+ifeq (@USE_SNDIO@,1)
+SRCS += Sinks/AESinkSNDIO.cpp
+endif
endif
SRCS += DSPAddons/ActiveAEDSP.cpp
diff --git xbmc/cores/AudioEngine/Sinks/AESinkSNDIO.cpp xbmc/cores/AudioEngine/Sinks/AESinkSNDIO.cpp
new file mode 100644
index 0000000000..879b9a90a3
--- /dev/null
+++ xbmc/cores/AudioEngine/Sinks/AESinkSNDIO.cpp
@@ -0,0 +1,313 @@
+/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#ifdef HAS_SNDIO
+#include "AESinkSNDIO.h"
+#include "cores/AudioEngine/Utils/AEUtil.h"
+#include "utils/log.h"
+
+#include <sys/param.h>
+
+#ifndef nitems
+#define nitems(x) (sizeof((x))/sizeof((x)[0]))
+#endif
+
+static enum AEChannel channelMap[] =
+{
+ AE_CH_FL,
+ AE_CH_FR,
+ AE_CH_BL,
+ AE_CH_BR,
+ AE_CH_FC,
+ AE_CH_LFE,
+ AE_CH_SL,
+ AE_CH_SR,
+};
+
+struct sndio_formats
+{
+ AEDataFormat fmt;
+ unsigned int bits;
+ unsigned int bps;
+ unsigned int sig;
+ unsigned int le;
+ unsigned int msb;
+};
+
+static struct sndio_formats formats[] =
+{
+ { AE_FMT_S32LE, 32, 4, 1, 1, 1 },
+ { AE_FMT_S32BE, 32, 4, 1, 0, 1 },
+
+ { AE_FMT_S24LE4, 24, 4, 1, 1, 0 },
+ { AE_FMT_S24BE4, 24, 4, 1, 0, 0 },
+ { AE_FMT_S24LE4, 24, 4, 1, 1, 1 },
+ { AE_FMT_S24BE4, 24, 4, 1, 0, 1 },
+
+ { AE_FMT_S24LE3, 24, 3, 1, 1, 0 },
+ { AE_FMT_S24BE3, 24, 3, 1, 0, 0 },
+ { AE_FMT_S24LE3, 24, 3, 1, 1, 1 },
+ { AE_FMT_S24BE3, 24, 3, 1, 0, 1 },
+
+ { AE_FMT_S16LE, 16, 2, 1, 1, 1 },
+ { AE_FMT_S16LE, 16, 2, 1, 1, 0 },
+ { AE_FMT_S16BE, 16, 2, 1, 0, 1 },
+ { AE_FMT_S16BE, 16, 2, 1, 0, 0 },
+
+ { AE_FMT_U8, 8, 1, 0, 0, 0 },
+ { AE_FMT_U8, 8, 1, 0, 0, 1 },
+ { AE_FMT_U8, 8, 1, 0, 1, 0 },
+ { AE_FMT_U8, 8, 1, 0, 1, 1 },
+};
+
+static AEDataFormat lookupDataFormat(int bits, int bps, int sig, int le, int msb)
+{
+ for (size_t i = 0; i < nitems(formats); i++)
+ {
+ if (bits == formats[i].bits &&
+ bps == formats[i].bps &&
+ sig == formats[i].sig &&
+ le == formats[i].le &&
+ msb == formats[i].msb)
+ {
+ return formats[i].fmt;
+ }
+ }
+ return AE_FMT_INVALID;
+}
+
+void CAESinkSNDIO::AudioFormatToPar(AEAudioFormat& format)
+{
+ sio_initpar(&par);
+
+ par.rate = format.m_sampleRate;
+ par.xrun = SIO_IGNORE;
+ par.pchan = format.m_channelLayout.Count();
+
+ for (size_t i = 0; i < nitems(formats); i++)
+ {
+ if (formats[i].fmt == format.m_dataFormat)
+ {
+ par.bits = formats[i].bits;
+ par.sig = formats[i].sig;
+ par.le = formats[i].le;
+ par.msb = formats[i].msb;
+ par.bps = formats[i].bps;
+ return;
+ }
+ }
+
+ /* Default to AE_FMT_S16LE */
+ par.bits = 16;
+ par.bps = SIO_BPS(16);
+ par.sig = 1;
+ par.le = 1;
+ par.msb = 0;
+}
+
+bool CAESinkSNDIO::ParToAudioFormat(AEAudioFormat& format)
+{
+ AEDataFormat dataFormat = lookupDataFormat(par.bits, par.bps, par.sig, par.le, par.msb);
+ if (dataFormat == AE_FMT_INVALID)
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::ParToAudioFormat - invalid data format");
+ return false;
+ }
+
+ if (par.pchan > nitems(channelMap))
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::ParToAudioFormat - too many channels: %d", par.pchan);
+ return false;
+ }
+
+ CAEChannelInfo info;
+ for (unsigned int i = 0; i < par.pchan; i++)
+ info += channelMap[i];
+ format.m_channelLayout = info;
+ format.m_dataFormat = dataFormat;
+ format.m_sampleRate = par.rate;
+ format.m_frameSize = par.bps * par.pchan;
+ format.m_frames = par.bufsz / format.m_frameSize;
+
+ return true;
+}
+
+CAESinkSNDIO::CAESinkSNDIO()
+{
+ m_hdl = nullptr;
+}
+
+CAESinkSNDIO::~CAESinkSNDIO()
+{
+ Deinitialize();
+}
+
+bool CAESinkSNDIO::Initialize(AEAudioFormat &format, std::string &device)
+{
+ if ((m_hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0)) == nullptr)
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::Initialize - Failed to open device");
+ return false;
+ }
+
+ AudioFormatToPar(format);
+ if (!sio_setpar(m_hdl, &par) ||
+ !sio_getpar(m_hdl, &par) ||
+ !ParToAudioFormat(format))
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::Initialize - could not negotiate parameters");
+ return false;
+ }
+
+ played = written = 0;
+
+ sio_onmove(m_hdl, CAESinkSNDIO::OnmoveCb, this);
+
+ if (!sio_start(m_hdl))
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::Initialize - sio_start failed");
+ return false;
+ }
+
+ return true;
+}
+
+void CAESinkSNDIO::Deinitialize()
+{
+ if (m_hdl != nullptr)
+ {
+ sio_close(m_hdl);
+ m_hdl = nullptr;
+ }
+}
+
+void CAESinkSNDIO::Stop()
+{
+ if (!m_hdl)
+ return;
+
+ if (!sio_stop(m_hdl))
+ CLog::Log(LOGERROR, "CAESinkSNDIO::Stop - Failed");
+
+ written = played = 0;
+}
+
+void CAESinkSNDIO::OnmoveCb(void *arg, int delta) {
+ CAESinkSNDIO* self = static_cast<CAESinkSNDIO*>(arg);
+ self->played += delta;
+}
+
+void CAESinkSNDIO::GetDelay(AEDelayStatus& status)
+{
+ unsigned int frameSize = par.bps * par.pchan;
+ double delay = 1.0 * ((written / frameSize) - played) / par.rate;
+ status.SetDelay(delay);
+}
+
+unsigned int CAESinkSNDIO::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
+{
+ if (!m_hdl)
+ return INT_MAX;
+
+ unsigned int frameSize = par.bps * par.pchan;
+ size_t size = frames * frameSize;
+ void *buffer = data[0] + offset * frameSize;
+ size_t wrote = sio_write(m_hdl, buffer, size);
+ written += wrote;
+ return wrote / frameSize;
+}
+
+void CAESinkSNDIO::Drain()
+{
+ if(!m_hdl)
+ return;
+
+ if (!sio_stop(m_hdl) || !sio_start(m_hdl))
+ CLog::Log(LOGERROR, "CAESinkSNDIO::Drain - failed");
+
+ written = played = 0;
+}
+
+void CAESinkSNDIO::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
+{
+ struct sio_hdl *hdl;
+ struct sio_cap cap;
+
+ if ((hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0)) == nullptr)
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::EnumerateDevicesEx - sio_open");
+ return;
+ }
+
+ if (!sio_getcap(hdl, &cap))
+ {
+ CLog::Log(LOGERROR, "CAESinkSNDIO::EnumerateDevicesEx - sio_getcap");
+ return;
+ }
+
+ sio_close(hdl);
+ hdl = nullptr;
+
+ for (unsigned int i = 0; i < cap.nconf; i++)
+ {
+ CAEDeviceInfo info;
+ sio_cap::sio_conf conf = cap.confs[i];
+
+ info.m_deviceName = SIO_DEVANY;
+ info.m_displayName = "sndio";
+ info.m_displayNameExtra = "#" + std::to_string(i);
+ info.m_deviceType = AE_DEVTYPE_PCM;
+
+ unsigned int maxchan = 0;
+ for (unsigned int j = 0; j < SIO_NCHAN; j++)
+ {
+ if (conf.pchan & (1 << j))
+ maxchan = MAX(maxchan, cap.pchan[j]);
+ }
+
+ maxchan = MIN(maxchan, nitems(channelMap));
+ for (unsigned int j = 0; j < maxchan; j++)
+ info.m_channels += channelMap[j];
+
+ for (unsigned int j = 0; j < SIO_NRATE; j++)
+ {
+ if (conf.rate & (1 << j))
+ {
+ info.m_sampleRates.push_back(cap.rate[j]);
+ }
+ }
+
+ for (unsigned int j = 0; j < SIO_NENC; j++)
+ {
+ if (conf.enc & (1 << j))
+ {
+ AEDataFormat format = lookupDataFormat(cap.enc[j].bits, cap.enc[j].bps, cap.enc[j].sig, cap.enc[j].le, cap.enc[j].msb);
+ if (format != AE_FMT_INVALID)
+ info.m_dataFormats.push_back(format);
+ }
+ }
+
+ list.push_back(info);
+ }
+}
+
+#endif
diff --git xbmc/cores/AudioEngine/Sinks/AESinkSNDIO.h xbmc/cores/AudioEngine/Sinks/AESinkSNDIO.h
new file mode 100644
index 0000000000..55719cd305
--- /dev/null
+++ xbmc/cores/AudioEngine/Sinks/AESinkSNDIO.h
@@ -0,0 +1,57 @@
+/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+#pragma once
+/*
+ * Copyright (C) 2010-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program 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, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cores/AudioEngine/Interfaces/AESink.h"
+#include "cores/AudioEngine/Utils/AEDeviceInfo.h"
+#include <stdint.h>
+#include <sndio.h>
+
+#include "threads/CriticalSection.h"
+
+class CAESinkSNDIO : public IAESink
+{
+public:
+ virtual const char *GetName() { return "sndio"; }
+
+ CAESinkSNDIO();
+ virtual ~CAESinkSNDIO();
+
+ virtual bool Initialize(AEAudioFormat &format, std::string &device);
+ virtual void Deinitialize();
+
+ virtual void Stop();
+ virtual void GetDelay(AEDelayStatus& status);
+ virtual double GetCacheTotal() { return 0.0; }
+ virtual unsigned int AddPackets(uint8_t **data, unsigned int frames, unsigned int offset);
+ virtual void Drain();
+ static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
+private:
+ void AudioFormatToPar(AEAudioFormat& format);
+ bool ParToAudioFormat(AEAudioFormat& format);
+ static void OnmoveCb(void *arg, int delta);
+
+ struct sio_hdl *m_hdl;
+ struct sio_par par;
+ ssize_t played;
+ ssize_t written;
+};
+
diff --git xbmc/system.h xbmc/system.h
index d426ade093..ed01ab53fd 100644
--- xbmc/system.h
+++ xbmc/system.h
@@ -172,6 +172,9 @@
#ifdef HAVE_LIBPULSE
#define HAS_PULSEAUDIO
#endif
+#ifdef HAVE_SNDIO
+#define HAS_SNDIO
+#endif
#ifdef HAVE_ALSA
#define HAS_ALSA
#endif