mpd: switch to using libnpupnp
authorRosen Penev <rosenp@gmail.com>
Sun, 23 Aug 2020 23:19:48 +0000 (16:19 -0700)
committerRosen Penev <rosenp@gmail.com>
Thu, 17 Sep 2020 01:13:32 +0000 (18:13 -0700)
Removed iconv const patch. After discussing with upstream. it turns out
that libiconv-full in OpenWrt is broken.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
sound/mpd/Makefile
sound/mpd/patches/010-iconv.patch
sound/mpd/patches/020-iconv-const.patch [deleted file]
sound/mpd/patches/020-npupnp.patch [new file with mode: 0644]

index b9d1e520d6e5c63cf877fd6fc3ee4a4f760d3c1d..f9701b30ab7cc1b0f3d3147050e2f094cf020abf 100644 (file)
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=mpd
 PKG_VERSION:=0.21.25
-PKG_RELEASE:=4
+PKG_RELEASE:=5
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
 PKG_SOURCE_URL:=https://www.musicpd.org/download/mpd/0.21/
@@ -47,7 +47,7 @@ endef
 define Package/mpd-full
 $(call Package/mpd/Default)
   TITLE+= (full)
-  DEPENDS+= +AUDIO_SUPPORT:pulseaudio-daemon +libvorbis +libmms +libupnp +libshout +yajl \
+  DEPENDS+= +AUDIO_SUPPORT:pulseaudio-daemon +libvorbis +libmms +libnpupnp +libshout +yajl \
             +BUILD_PATENTED:libffmpeg +!BUILD_PATENTED:libmad
   PROVIDES:=mpd
   VARIANT:=full
@@ -169,7 +169,7 @@ MESON_ARGS += \
 ifeq ($(BUILD_VARIANT),full)
 
   MESON_ARGS += \
-       -Dupnp=enabled \
+       -Dupnp=npupnp \
        -Dmms=enabled \
        -Dsoundcloud=enabled \
        -Dffmpeg=$(if $(CONFIG_BUILD_PATENTED),en,dis)abled \
index c7dad5fd5da18377c85af06e57c3ee4c30b276b4..81adf1ba74acdd0ca4ca5a5392a0615fae94eb6c 100644 (file)
@@ -41,6 +41,3 @@ index bd6e30944..132e15b89 100644
    if not have_iconv and get_option('iconv').enabled()
      error('iconv() not available')
    endif
--- 
-2.17.1
-
diff --git a/sound/mpd/patches/020-iconv-const.patch b/sound/mpd/patches/020-iconv-const.patch
deleted file mode 100644 (file)
index 82f19dd..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-From c2da1d47eeaf83d3683555b965a16654561f14b3 Mon Sep 17 00:00:00 2001
-From: Rosen Penev <rosenp@gmail.com>
-Date: Thu, 30 Jul 2020 16:27:02 -0700
-Subject: [PATCH] icu: fix compilation with const char iconv
-
-libiconv uses const char. Test for it and use it properly to fix
-compilation.
-
-Signed-off-by: Rosen Penev <rosenp@gmail.com>
----
- src/lib/icu/Converter.cxx |  2 +-
- src/lib/icu/meson.build   | 10 ++++++++++
- 2 files changed, 11 insertions(+), 1 deletion(-)
-
-diff --git a/src/lib/icu/Converter.cxx b/src/lib/icu/Converter.cxx
-index b03543a82..4c459e57e 100644
---- a/src/lib/icu/Converter.cxx
-+++ b/src/lib/icu/Converter.cxx
-@@ -83,7 +83,7 @@ DoConvert(iconv_t conv, const char *src)
- {
-       // TODO: dynamic buffer?
-       char buffer[4096];
--      char *in = const_cast<char *>(src);
-+      ICONV_CONST char *in = (ICONV_CONST char *)(src);
-       char *out = buffer;
-       size_t in_left = strlen(src);
-       size_t out_left = sizeof(buffer);
-diff --git a/src/lib/icu/meson.build b/src/lib/icu/meson.build
-index 132e15b89..ac7d1b72a 100644
---- a/src/lib/icu/meson.build
-+++ b/src/lib/icu/meson.build
-@@ -30,6 +30,16 @@ elif not get_option('iconv').disabled()
-     have_iconv = true
-     conf.set('HAVE_ICONV', have_iconv)
-   endif
-+  if have_iconv
-+    iconvconsttest = '''#include <iconv.h>
-+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
-+'''
-+    if c_compiler.compiles(iconvconsttest, dependencies : libiconv)
-+      conf.set('ICONV_CONST', '')
-+    else
-+      conf.set('ICONV_CONST', 'const')
-+    endif
-+  endif
-   if not have_iconv and get_option('iconv').enabled()
-     error('iconv() not available')
-   endif
--- 
-2.17.1
-
diff --git a/sound/mpd/patches/020-npupnp.patch b/sound/mpd/patches/020-npupnp.patch
new file mode 100644 (file)
index 0000000..02d2556
--- /dev/null
@@ -0,0 +1,436 @@
+From 61df54155a3cb1846e6bf15e4f007ec8d623de63 Mon Sep 17 00:00:00 2001
+From: Jean-Francois Dockes <jf@dockes.org>
+Date: Sun, 23 Aug 2020 14:22:21 +0200
+Subject: [PATCH] Modification to use npupnp instead of pupnp when the upnp
+ meson option is set
+
+---
+ meson_options.txt                             |   5 +-
+ .../plugins/upnp/ContentDirectoryService.cxx  | 101 ++++++++++++++++++
+ src/lib/upnp/Action.hxx                       |   2 +
+ src/lib/upnp/ClientInit.cxx                   |  12 +--
+ src/lib/upnp/Compat.hxx                       |   4 +-
+ src/lib/upnp/ContentDirectoryService.cxx      |  25 +++++
+ src/lib/upnp/Init.cxx                         |   4 +
+ src/lib/upnp/UniqueIxml.hxx                   |   2 +
+ src/lib/upnp/ixmlwrap.cxx                     |   4 +
+ src/lib/upnp/ixmlwrap.hxx                     |   2 +
+ src/lib/upnp/meson.build                      |  20 +++-
+ 11 files changed, 170 insertions(+), 11 deletions(-)
+
+diff --git a/meson_options.txt b/meson_options.txt
+index d17ac1ca8..da90ccfd8 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -54,7 +54,10 @@ option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio
+ #
+ option('database', type: 'boolean', value: true, description: 'enable support for the music database')
+-option('upnp', type: 'feature', description: 'UPnP client support')
++option('upnp', type: 'combo',
++       choices: ['auto', 'pupnp', 'npupnp', 'disabled'],
++       value: 'auto',
++       description: 'UPnP client support')
+ option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
+ #
+diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx
+index 99893d89d..29d58ca23 100644
+--- a/src/db/plugins/upnp/ContentDirectoryService.cxx
++++ b/src/db/plugins/upnp/ContentDirectoryService.cxx
+@@ -18,7 +18,10 @@
+  */
+ #include "lib/upnp/ContentDirectoryService.hxx"
++#include "config.h"
++#ifdef USING_PUPNP
+ #include "lib/upnp/ixmlwrap.hxx"
++#endif
+ #include "lib/upnp/UniqueIxml.hxx"
+ #include "lib/upnp/Action.hxx"
+ #include "Directory.hxx"
+@@ -28,8 +31,11 @@
+ #include "util/ScopeExit.hxx"
+ #include "util/StringFormat.hxx"
++#include <algorithm>
++
+ #include <stdio.h>
++#ifdef USING_PUPNP
+ static void
+ ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
+ {
+@@ -39,6 +45,7 @@ ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
+       dirbuf.Parse(p);
+ }
++#endif
+ inline void
+ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
+@@ -47,6 +54,7 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
+                                     unsigned &didreadp,
+                                     unsigned &totalp) const
+ {
++#ifdef USING_PUPNP
+       // Some devices require an empty SortCriteria, else bad params
+       IXML_Document *request =
+               MakeActionHelper("Browse", m_serviceType.c_str(),
+@@ -82,6 +90,37 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
+               totalp = ParseUnsigned(value);
+       ReadResultTag(dirbuf, response);
++#else
++      std::vector<std::pair<std::string, std::string> > actionParams{
++              { "ObjectID", objectId },
++              { "BrowseFlag", "BrowseDirectChildren" },
++              { "Filter", "*" },
++              { "SortCriteria", "" },
++              { "StartingIndex", StringFormat<32>("%u", offset).c_str() },
++              { "RequestedCount", StringFormat<32>("%u", count).c_str() }
++      };
++      std::vector<std::pair<std::string, std::string> > responseData;
++      int errcode;
++      std::string errdesc;
++      int code =
++              UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
++                             actionParams, responseData, &errcode, errdesc);
++      if (code != UPNP_E_SUCCESS)
++              throw FormatRuntimeError("UpnpSendAction() failed: %s",
++                                       UpnpGetErrorMessage(code));
++      const char *p = "";
++      didreadp = 0;
++      for (const auto &entry : responseData) {
++              if (entry.first == "Result") {
++                      p = entry.second.c_str();
++              } else if (entry.first == "TotalMatches") {
++                      totalp = ParseUnsigned(entry.second.c_str());
++              } else if (entry.first == "NumberReturned") {
++                      didreadp = ParseUnsigned(entry.second.c_str());
++              }
++      }
++      dirbuf.Parse(p);
++#endif
+ }
+ UPnPDirContent
+@@ -110,6 +149,7 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
+       unsigned offset = 0, total = -1, count;
+       do {
++#ifdef USING_PUPNP
+               UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
+                                                           "ContainerID", objectId,
+                                                           "SearchCriteria", ss,
+@@ -147,6 +187,39 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
+                       total = ParseUnsigned(value);
+               ReadResultTag(dirbuf, response.get());
++#else
++              std::vector<std::pair<std::string, std::string> > actionParams{
++                      { "ContainerID", objectId },
++                      { "SearchCriteria", ss },
++                      { "Filter", "*" },
++                      { "SortCriteria", "" },
++                      { "StartingIndex",
++                        StringFormat<32>("%u", offset).c_str() },
++                      { "RequestedCount", "0" }
++              };
++              std::vector<std::pair<std::string, std::string> > responseData;
++              int errcode;
++              std::string errdesc;
++              int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
++                                        "Search", actionParams, responseData,
++                                        &errcode, errdesc);
++              if (code != UPNP_E_SUCCESS)
++                      throw FormatRuntimeError("UpnpSendAction() failed: %s",
++                                               UpnpGetErrorMessage(code));
++              const char *p = "";
++              count = 0;
++              for (const auto &entry : responseData) {
++                      if (entry.first == "Result") {
++                              p = entry.second.c_str();
++                      } else if (entry.first == "TotalMatches") {
++                              total = ParseUnsigned(entry.second.c_str());
++                      } else if (entry.first == "NumberReturned") {
++                              count = ParseUnsigned(entry.second.c_str());
++                              offset += count;
++                      }
++              }
++              dirbuf.Parse(p);
++#endif
+       } while (count > 0 && offset < total);
+       return dirbuf;
+@@ -156,6 +229,7 @@ UPnPDirContent
+ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
+                                    const char *objectId) const
+ {
++#ifdef USING_PUPNP
+       // Create request
+       UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
+                                                   "ObjectID", objectId,
+@@ -179,4 +253,31 @@ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
+       UPnPDirContent dirbuf;
+       ReadResultTag(dirbuf, response.get());
+       return dirbuf;
++#else
++      std::vector<std::pair<std::string, std::string> > actionParams{
++              { "ObjectID", objectId }, { "BrowseFlag", "BrowseMetadata" },
++              { "Filter", "*" },        { "SortCriteria", "" },
++              { "StartingIndex", "0" }, { "RequestedCount", "1" }
++      };
++      std::vector<std::pair<std::string, std::string> > responseData;
++      int errcode;
++      std::string errdesc;
++      int code =
++              UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
++                             actionParams, responseData, &errcode, errdesc);
++      if (code != UPNP_E_SUCCESS)
++              throw FormatRuntimeError("UpnpSendAction() failed: %s",
++                                       UpnpGetErrorMessage(code));
++      const char *p = "";
++      for (const auto &entry : responseData) {
++              if (entry.first == "Result") {
++                      p = entry.second.c_str();
++                      break;
++              }
++      }
++
++      UPnPDirContent dirbuf;
++      dirbuf.Parse(p);
++      return dirbuf;
++#endif
+ }
+diff --git a/src/lib/upnp/Action.hxx b/src/lib/upnp/Action.hxx
+index 49ed75198..4ecf4cb06 100644
+--- a/src/lib/upnp/Action.hxx
++++ b/src/lib/upnp/Action.hxx
+@@ -38,6 +38,7 @@ CountNameValuePairs(gcc_unused const char *name, gcc_unused const char *value,
+       return 1 + CountNameValuePairs(args...);
+ }
++#ifdef USING_PUPNP
+ /**
+  * A wrapper for UpnpMakeAction() that counts the number of name/value
+  * pairs and adds the nullptr sentinel.
+@@ -52,5 +53,6 @@ MakeActionHelper(const char *action_name, const char *service_type,
+                             args...,
+                             nullptr, nullptr);
+ }
++#endif
+ #endif
+diff --git a/src/lib/upnp/ClientInit.cxx b/src/lib/upnp/ClientInit.cxx
+index 23ba9cade..54b677fa2 100644
+--- a/src/lib/upnp/ClientInit.cxx
++++ b/src/lib/upnp/ClientInit.cxx
+@@ -31,14 +31,12 @@ static Mutex upnp_client_init_mutex;
+ static unsigned upnp_client_ref;
+ static UpnpClient_Handle upnp_client_handle;
+-static int
+-UpnpClientCallback(Upnp_EventType et,
+-#if UPNP_VERSION >= 10800
+-                 const
++static int UpnpClientCallback(Upnp_EventType et,
++#if 1
++                            const
+ #endif
+-                 void *evp,
+-                 void *cookie) noexcept
+-{
++                            void *evp,
++                            void *cookie) noexcept {
+       if (cookie == nullptr)
+               /* this is the cookie passed to UpnpRegisterClient();
+                  but can this ever happen?  Will libupnp ever invoke
+diff --git a/src/lib/upnp/Compat.hxx b/src/lib/upnp/Compat.hxx
+index 7fba1d83b..b9a4d7cf3 100644
+--- a/src/lib/upnp/Compat.hxx
++++ b/src/lib/upnp/Compat.hxx
+@@ -22,14 +22,14 @@
+ #include <upnp.h>
+-#if UPNP_VERSION < 10800
++#if 0
+ /* emulate the libupnp 1.8 API with older versions */
+ using UpnpDiscovery = Upnp_Discovery;
+ #endif
+-#if UPNP_VERSION < 10624
++#if 0
+ #include "util/Compiler.h"
+ gcc_pure
+diff --git a/src/lib/upnp/ContentDirectoryService.cxx b/src/lib/upnp/ContentDirectoryService.cxx
+index ae514c717..eed28b41a 100644
+--- a/src/lib/upnp/ContentDirectoryService.cxx
++++ b/src/lib/upnp/ContentDirectoryService.cxx
+@@ -17,15 +17,21 @@
+  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+  */
++#include "config.h"
++
+ #include "ContentDirectoryService.hxx"
+ #include "UniqueIxml.hxx"
+ #include "Device.hxx"
++#ifdef USING_PUPNP
+ #include "ixmlwrap.hxx"
++#endif
+ #include "Action.hxx"
+ #include "util/UriUtil.hxx"
+ #include "util/RuntimeError.hxx"
+ #include "util/SplitString.hxx"
++#include <algorithm>
++
+ ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device,
+                                                const UPnPService &service) noexcept
+       :m_actionURL(uri_apply_base(service.controlURL, device.URLBase)),
+@@ -51,6 +57,7 @@ ContentDirectoryService::~ContentDirectoryService() noexcept
+ std::forward_list<std::string>
+ ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
+ {
++#ifdef USING_PUPNP
+       UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
+                                                 0,
+                                                 nullptr, nullptr));
+@@ -69,6 +76,24 @@ ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
+       const char *s = ixmlwrap::getFirstElementValue(response.get(),
+                                                      "SearchCaps");
++#else
++      std::vector<std::pair<std::string, std::string> > responseData;
++      int errcode;
++      std::string errdesc;
++      auto code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
++                                 "GetSearchCapabilities", {}, responseData,
++                                 &errcode, errdesc);
++      if (code != UPNP_E_SUCCESS)
++              throw FormatRuntimeError("UpnpSendAction() failed: %s",
++                                       UpnpGetErrorMessage(code));
++      const char *s{ nullptr };
++      for (auto &entry : responseData) {
++              if (entry.first == "SearchCaps") {
++                      s = entry.second.c_str();
++                      break;
++              }
++      }
++#endif
+       if (s == nullptr || *s == 0)
+               /* we could just "return {}" here, but GCC 5 doesn't
+                  understand that */
+diff --git a/src/lib/upnp/Init.cxx b/src/lib/upnp/Init.cxx
+index 7ad4d565a..10510402a 100644
+--- a/src/lib/upnp/Init.cxx
++++ b/src/lib/upnp/Init.cxx
+@@ -23,7 +23,9 @@
+ #include <upnp.h>
+ #include <upnptools.h>
++#ifdef USING_PUPNP
+ #include <ixml.h>
++#endif
+ #include <assert.h>
+@@ -44,8 +46,10 @@ DoInit()
+       UpnpSetMaxContentLength(2000*1024);
++#ifdef USING_PUPNP
+       // Servers sometimes make error (e.g.: minidlna returns bad utf-8)
+       ixmlRelaxParser(1);
++#endif
+ }
+ void
+diff --git a/src/lib/upnp/UniqueIxml.hxx b/src/lib/upnp/UniqueIxml.hxx
+index 2ff2afa62..8a0ea0a1f 100644
+--- a/src/lib/upnp/UniqueIxml.hxx
++++ b/src/lib/upnp/UniqueIxml.hxx
+@@ -20,6 +20,7 @@
+ #ifndef MPD_UPNP_UNIQUE_XML_HXX
+ #define MPD_UPNP_UNIQUE_XML_HXX
++#ifdef USING_PUPNP
+ #include <ixml.h>
+ #include <memory>
+@@ -37,4 +38,5 @@ struct UpnpIxmlDeleter {
+ typedef std::unique_ptr<IXML_Document, UpnpIxmlDeleter> UniqueIxmlDocument;
+ typedef std::unique_ptr<IXML_NodeList, UpnpIxmlDeleter> UniqueIxmlNodeList;
++#endif /* USING_PUPNP */
+ #endif
+diff --git a/src/lib/upnp/ixmlwrap.cxx b/src/lib/upnp/ixmlwrap.cxx
+index 4e44f35a6..c7798e557 100644
+--- a/src/lib/upnp/ixmlwrap.cxx
++++ b/src/lib/upnp/ixmlwrap.cxx
+@@ -15,6 +15,9 @@
+  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  */
++#include "config.h"
++
++#ifdef USING_PUPNP
+ #include "ixmlwrap.hxx"
+ #include "UniqueIxml.hxx"
+@@ -39,3 +42,4 @@ getFirstElementValue(IXML_Document *doc, const char *name) noexcept
+ }
+ }
++#endif
+diff --git a/src/lib/upnp/ixmlwrap.hxx b/src/lib/upnp/ixmlwrap.hxx
+index 6713d59bd..4b01801f7 100644
+--- a/src/lib/upnp/ixmlwrap.hxx
++++ b/src/lib/upnp/ixmlwrap.hxx
+@@ -17,6 +17,7 @@
+ #ifndef _IXMLWRAP_H_INCLUDED_
+ #define _IXMLWRAP_H_INCLUDED_
++#ifdef USING_PUPNP
+ #include <ixml.h>
+ #include <string>
+@@ -32,4 +33,5 @@ namespace ixmlwrap {
+ }
++#endif /* USING_PUPNP */
+ #endif /* _IXMLWRAP_H_INCLUDED_ */
+diff --git a/src/lib/upnp/meson.build b/src/lib/upnp/meson.build
+index 9e16f7319..bdc248e6c 100644
+--- a/src/lib/upnp/meson.build
++++ b/src/lib/upnp/meson.build
+@@ -1,4 +1,22 @@
+-upnp_dep = dependency('libupnp', required: get_option('upnp'))
++upnp_option = get_option('upnp')
++
++if upnp_option == 'auto'
++  upnp_dep = dependency('libupnp', version: '>= 1.8', required: false)
++  conf.set('USING_PUPNP', upnp_dep.found())
++  if not upnp_dep.found()
++    upnp_dep = dependency('libnpupnp', version: '>= 1.8', required: false)
++  endif
++elif upnp_option == 'pupnp'
++  upnp_dep = dependency('libupnp', version: '>= 1.8', required: true)
++  conf.set('USING_PUPNP', true)
++elif upnp_option == 'npupnp'
++  upnp_dep = dependency('libnpupnp', required: true)
++  conf.set('USING_PUPNP', false)
++elif upnp_option == 'disabled'
++  upnp_dep = dependency('', required: false)
++  subdir_done()
++endif
++
+ conf.set('ENABLE_UPNP', upnp_dep.found())
+ if not upnp_dep.found()
+   subdir_done()