Merge branch 'npu' of git://github.com/neheb/MPD
This commit is contained in:
commit
d747576793
1
NEWS
1
NEWS
|
@ -6,6 +6,7 @@ ver 0.23 (not yet released)
|
||||||
- proxy: require MPD 0.20 or later
|
- proxy: require MPD 0.20 or later
|
||||||
- proxy: require libmpdclient 2.11 or later
|
- proxy: require libmpdclient 2.11 or later
|
||||||
- proxy: split search into chunks to avoid exceeding the output buffer
|
- proxy: split search into chunks to avoid exceeding the output buffer
|
||||||
|
- upnp: support libnpupnp instead of libupnp
|
||||||
* output
|
* output
|
||||||
- pipewire: new plugin
|
- pipewire: new plugin
|
||||||
- snapcast: new plugin
|
- snapcast: new plugin
|
||||||
|
|
|
@ -62,7 +62,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('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)')
|
option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lib/upnp/ContentDirectoryService.hxx"
|
#include "lib/upnp/ContentDirectoryService.hxx"
|
||||||
#include "lib/upnp/ixmlwrap.hxx"
|
#include "config.h"
|
||||||
|
#ifdef USING_PUPNP
|
||||||
|
# include "lib/upnp/ixmlwrap.hxx"
|
||||||
|
#endif
|
||||||
#include "lib/upnp/UniqueIxml.hxx"
|
#include "lib/upnp/UniqueIxml.hxx"
|
||||||
#include "lib/upnp/Action.hxx"
|
#include "lib/upnp/Action.hxx"
|
||||||
#include "Directory.hxx"
|
#include "Directory.hxx"
|
||||||
|
@ -27,6 +30,9 @@
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
#include "util/StringFormat.hxx"
|
#include "util/StringFormat.hxx"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifdef USING_PUPNP
|
||||||
static void
|
static void
|
||||||
ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
|
ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
|
||||||
{
|
{
|
||||||
|
@ -36,6 +42,7 @@ ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
|
||||||
|
|
||||||
dirbuf.Parse(p);
|
dirbuf.Parse(p);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
|
ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
|
||||||
|
@ -44,6 +51,7 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
|
||||||
unsigned &didreadp,
|
unsigned &didreadp,
|
||||||
unsigned &totalp) const
|
unsigned &totalp) const
|
||||||
{
|
{
|
||||||
|
#ifdef USING_PUPNP
|
||||||
// Some devices require an empty SortCriteria, else bad params
|
// Some devices require an empty SortCriteria, else bad params
|
||||||
IXML_Document *request =
|
IXML_Document *request =
|
||||||
MakeActionHelper("Browse", m_serviceType.c_str(),
|
MakeActionHelper("Browse", m_serviceType.c_str(),
|
||||||
|
@ -79,6 +87,35 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
|
||||||
totalp = ParseUnsigned(value);
|
totalp = ParseUnsigned(value);
|
||||||
|
|
||||||
ReadResultTag(dirbuf, response);
|
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
|
UPnPDirContent
|
||||||
|
@ -107,6 +144,7 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
|
||||||
unsigned offset = 0, total = -1, count;
|
unsigned offset = 0, total = -1, count;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
#ifdef USING_PUPNP
|
||||||
UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
|
UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
|
||||||
"ContainerID", objectId,
|
"ContainerID", objectId,
|
||||||
"SearchCriteria", ss,
|
"SearchCriteria", ss,
|
||||||
|
@ -144,6 +182,36 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
|
||||||
total = ParseUnsigned(value);
|
total = ParseUnsigned(value);
|
||||||
|
|
||||||
ReadResultTag(dirbuf, response.get());
|
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);
|
} while (count > 0 && offset < total);
|
||||||
|
|
||||||
return dirbuf;
|
return dirbuf;
|
||||||
|
@ -153,6 +221,7 @@ UPnPDirContent
|
||||||
ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
|
ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
|
||||||
const char *objectId) const
|
const char *objectId) const
|
||||||
{
|
{
|
||||||
|
#ifdef USING_PUPNP
|
||||||
// Create request
|
// Create request
|
||||||
UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
|
UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
|
||||||
"ObjectID", objectId,
|
"ObjectID", objectId,
|
||||||
|
@ -176,4 +245,27 @@ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
|
||||||
UPnPDirContent dirbuf;
|
UPnPDirContent dirbuf;
|
||||||
ReadResultTag(dirbuf, response.get());
|
ReadResultTag(dirbuf, response.get());
|
||||||
return dirbuf;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UPnPDirContent dirbuf;
|
||||||
|
dirbuf.Parse(p);
|
||||||
|
return dirbuf;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ CountNameValuePairs([[maybe_unused]] const char *name, [[maybe_unused]] const ch
|
||||||
return 1 + CountNameValuePairs(args...);
|
return 1 + CountNameValuePairs(args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USING_PUPNP
|
||||||
/**
|
/**
|
||||||
* A wrapper for UpnpMakeAction() that counts the number of name/value
|
* A wrapper for UpnpMakeAction() that counts the number of name/value
|
||||||
* pairs and adds the nullptr sentinel.
|
* pairs and adds the nullptr sentinel.
|
||||||
|
@ -52,5 +53,6 @@ MakeActionHelper(const char *action_name, const char *service_type,
|
||||||
args...,
|
args...,
|
||||||
nullptr, nullptr);
|
nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,13 +17,21 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include "ContentDirectoryService.hxx"
|
#include "ContentDirectoryService.hxx"
|
||||||
#include "UniqueIxml.hxx"
|
|
||||||
#include "Device.hxx"
|
#include "Device.hxx"
|
||||||
#include "ixmlwrap.hxx"
|
#include "UniqueIxml.hxx"
|
||||||
#include "util/UriRelative.hxx"
|
#ifdef USING_PUPNP
|
||||||
#include "util/RuntimeError.hxx"
|
# include "ixmlwrap.hxx"
|
||||||
|
#endif
|
||||||
|
#include "Action.hxx"
|
||||||
#include "util/IterableSplitString.hxx"
|
#include "util/IterableSplitString.hxx"
|
||||||
|
#include "util/RuntimeError.hxx"
|
||||||
|
#include "util/UriRelative.hxx"
|
||||||
|
#include "util/UriUtil.hxx"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <upnptools.h>
|
#include <upnptools.h>
|
||||||
|
|
||||||
|
@ -50,6 +58,7 @@ ContentDirectoryService::~ContentDirectoryService() noexcept = default;
|
||||||
std::forward_list<std::string>
|
std::forward_list<std::string>
|
||||||
ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
|
ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
|
||||||
{
|
{
|
||||||
|
#ifdef USING_PUPNP
|
||||||
UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
|
UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
|
||||||
0,
|
0,
|
||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
|
@ -68,6 +77,23 @@ ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
|
||||||
|
|
||||||
const char *s = ixmlwrap::getFirstElementValue(response.get(),
|
const char *s = ixmlwrap::getFirstElementValue(response.get(),
|
||||||
"SearchCaps");
|
"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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (s == nullptr || *s == 0)
|
if (s == nullptr || *s == 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,9 @@
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
|
|
||||||
#include <upnptools.h>
|
#include <upnptools.h>
|
||||||
#include <ixml.h>
|
#ifdef USING_PUPNP
|
||||||
|
# include <ixml.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -44,8 +46,10 @@ DoInit()
|
||||||
|
|
||||||
UpnpSetMaxContentLength(2000*1024);
|
UpnpSetMaxContentLength(2000*1024);
|
||||||
|
|
||||||
|
#ifdef USING_PUPNP
|
||||||
// Servers sometimes make error (e.g.: minidlna returns bad utf-8)
|
// Servers sometimes make error (e.g.: minidlna returns bad utf-8)
|
||||||
ixmlRelaxParser(1);
|
ixmlRelaxParser(1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -20,9 +20,10 @@
|
||||||
#ifndef MPD_UPNP_UNIQUE_XML_HXX
|
#ifndef MPD_UPNP_UNIQUE_XML_HXX
|
||||||
#define MPD_UPNP_UNIQUE_XML_HXX
|
#define MPD_UPNP_UNIQUE_XML_HXX
|
||||||
|
|
||||||
#include <ixml.h>
|
#ifdef USING_PUPNP
|
||||||
|
# include <ixml.h>
|
||||||
|
|
||||||
#include <memory>
|
# include <memory>
|
||||||
|
|
||||||
struct UpnpIxmlDeleter {
|
struct UpnpIxmlDeleter {
|
||||||
void operator()(IXML_Document *doc) noexcept {
|
void operator()(IXML_Document *doc) noexcept {
|
||||||
|
@ -37,4 +38,5 @@ struct UpnpIxmlDeleter {
|
||||||
typedef std::unique_ptr<IXML_Document, UpnpIxmlDeleter> UniqueIxmlDocument;
|
typedef std::unique_ptr<IXML_Document, UpnpIxmlDeleter> UniqueIxmlDocument;
|
||||||
typedef std::unique_ptr<IXML_NodeList, UpnpIxmlDeleter> UniqueIxmlNodeList;
|
typedef std::unique_ptr<IXML_NodeList, UpnpIxmlDeleter> UniqueIxmlNodeList;
|
||||||
|
|
||||||
|
#endif /* USING_PUPNP */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,8 +15,11 @@
|
||||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ixmlwrap.hxx"
|
#include "config.h"
|
||||||
#include "UniqueIxml.hxx"
|
|
||||||
|
#ifdef USING_PUPNP
|
||||||
|
# include "ixmlwrap.hxx"
|
||||||
|
# include "UniqueIxml.hxx"
|
||||||
|
|
||||||
namespace ixmlwrap {
|
namespace ixmlwrap {
|
||||||
|
|
||||||
|
@ -39,3 +42,4 @@ getFirstElementValue(IXML_Document *doc, const char *name) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ixmlwrap
|
} // namespace ixmlwrap
|
||||||
|
#endif
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
#ifndef _IXMLWRAP_H_INCLUDED_
|
#ifndef _IXMLWRAP_H_INCLUDED_
|
||||||
#define _IXMLWRAP_H_INCLUDED_
|
#define _IXMLWRAP_H_INCLUDED_
|
||||||
|
|
||||||
#include <ixml.h>
|
#ifdef USING_PUPNP
|
||||||
|
# include <ixml.h>
|
||||||
|
|
||||||
namespace ixmlwrap {
|
namespace ixmlwrap {
|
||||||
/**
|
/**
|
||||||
|
@ -30,4 +31,5 @@ namespace ixmlwrap {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* USING_PUPNP */
|
||||||
#endif /* _IXMLWRAP_H_INCLUDED_ */
|
#endif /* _IXMLWRAP_H_INCLUDED_ */
|
||||||
|
|
|
@ -1,4 +1,22 @@
|
||||||
upnp_dep = dependency('libupnp', version: '>= 1.8', 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())
|
conf.set('ENABLE_UPNP', upnp_dep.found())
|
||||||
if not upnp_dep.found()
|
if not upnp_dep.found()
|
||||||
subdir_done()
|
subdir_done()
|
||||||
|
|
Loading…
Reference in New Issue