|
a/upmpd/upmpd.cxx |
|
b/upmpd/upmpd.cxx |
|
... |
|
... |
39 |
static const string dfltFriendlyName("UpMpd");
|
39 |
static const string dfltFriendlyName("UpMpd");
|
40 |
|
40 |
|
41 |
// The UPnP MPD frontend device with its 2 services
|
41 |
// The UPnP MPD frontend device with its 2 services
|
42 |
class UpMpd : public UpnpDevice {
|
42 |
class UpMpd : public UpnpDevice {
|
43 |
public:
|
43 |
public:
|
|
|
44 |
enum Options {
|
|
|
45 |
upmpdNone,
|
|
|
46 |
upmpdOwnQueue, // The MPD belongs to us, we shall clear it as we like
|
|
|
47 |
};
|
44 |
UpMpd(const string& deviceid, const unordered_map<string, string>& xmlfiles,
|
48 |
UpMpd(const string& deviceid, const unordered_map<string, string>& xmlfiles,
|
45 |
MPDCli *mpdcli);
|
49 |
MPDCli *mpdcli, Options opts = upmpdNone);
|
46 |
|
50 |
|
47 |
// RenderingControl
|
51 |
// RenderingControl
|
48 |
int setMute(const SoapArgs& sc, SoapData& data);
|
52 |
int setMute(const SoapArgs& sc, SoapData& data);
|
49 |
int getMute(const SoapArgs& sc, SoapData& data);
|
53 |
int getMute(const SoapArgs& sc, SoapData& data);
|
50 |
int setVolume(const SoapArgs& sc, SoapData& data, bool isDb);
|
54 |
int setVolume(const SoapArgs& sc, SoapData& data, bool isDb);
|
|
... |
|
... |
94 |
|
98 |
|
95 |
// Desired volume target. We may delay executing small volume
|
99 |
// Desired volume target. We may delay executing small volume
|
96 |
// changes to avoid saturating with small requests.
|
100 |
// changes to avoid saturating with small requests.
|
97 |
int m_desiredvolume;
|
101 |
int m_desiredvolume;
|
98 |
|
102 |
|
|
|
103 |
int m_options;
|
99 |
};
|
104 |
};
|
100 |
|
105 |
|
101 |
static const string serviceIdRender("urn:upnp-org:serviceId:RenderingControl");
|
106 |
static const string serviceIdRender("urn:upnp-org:serviceId:RenderingControl");
|
102 |
static const string serviceIdTransport("urn:upnp-org:serviceId:AVTransport");
|
107 |
static const string serviceIdTransport("urn:upnp-org:serviceId:AVTransport");
|
103 |
|
108 |
|
104 |
UpMpd::UpMpd(const string& deviceid,
|
109 |
UpMpd::UpMpd(const string& deviceid,
|
105 |
const unordered_map<string, string>& xmlfiles,
|
110 |
const unordered_map<string, string>& xmlfiles,
|
106 |
MPDCli *mpdcli)
|
111 |
MPDCli *mpdcli, Options opts)
|
107 |
: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_desiredvolume(-1)
|
112 |
: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_desiredvolume(-1),
|
|
|
113 |
m_options(opts)
|
108 |
{
|
114 |
{
|
109 |
addServiceType(serviceIdRender,
|
115 |
addServiceType(serviceIdRender,
|
110 |
"urn:schemas-upnp-org:service:RenderingControl:1");
|
116 |
"urn:schemas-upnp-org:service:RenderingControl:1");
|
111 |
{ auto bound = bind(&UpMpd::setMute, this, _1, _2);
|
117 |
{ auto bound = bind(&UpMpd::setMute, this, _1, _2);
|
112 |
addActionMapping("SetMute", bound);
|
118 |
addActionMapping("SetMute", bound);
|
|
... |
|
... |
627 |
it = setnext? sc.args.find("NextURIMetaData") :
|
633 |
it = setnext? sc.args.find("NextURIMetaData") :
|
628 |
sc.args.find("CurrentURIMetaData");
|
634 |
sc.args.find("CurrentURIMetaData");
|
629 |
if (it != sc.args.end())
|
635 |
if (it != sc.args.end())
|
630 |
metadata = it->second;
|
636 |
metadata = it->second;
|
631 |
|
637 |
|
|
|
638 |
if ((m_options & upmpdOwnQueue) && !setnext) {
|
|
|
639 |
// If we own the queue, just clear it before setting the
|
|
|
640 |
// track. Else it's difficult to impossible to prevent it
|
|
|
641 |
// from growing if upmpdcli restarts. If the option is not set, the
|
|
|
642 |
// user prefers to live with the issue.
|
|
|
643 |
m_mpdcli->clearQueue();
|
|
|
644 |
}
|
|
|
645 |
|
632 |
const struct MpdStatus &mpds = m_mpdcli->getStatus();
|
646 |
const struct MpdStatus &mpds = m_mpdcli->getStatus();
|
633 |
bool is_song = (mpds.state == MpdStatus::MPDS_PLAY) ||
|
647 |
bool is_song = (mpds.state == MpdStatus::MPDS_PLAY) ||
|
634 |
(mpds.state == MpdStatus::MPDS_PAUSE);
|
648 |
(mpds.state == MpdStatus::MPDS_PAUSE);
|
635 |
int curpos = mpds.songpos;
|
649 |
int curpos = mpds.songpos;
|
636 |
LOGDEB("UpMpd::set" << (setnext?"Next":"") <<
|
650 |
LOGDEB("UpMpd::set" << (setnext?"Next":"") <<
|
637 |
"AVTransportURI: curpos: " <<
|
651 |
"AVTransportURI: curpos: " <<
|
638 |
curpos << " is_song " << is_song << " qlen " << mpds.qlen << endl);
|
652 |
curpos << " is_song " << is_song << " qlen " << mpds.qlen << endl);
|
639 |
|
653 |
|
640 |
// curpos == -1 means that the playlist was cleared or we just started. A
|
654 |
// curpos == -1 means that the playlist was cleared or we just started. A
|
641 |
// play will use position 0, so it's actually equivalent to curpos == 0
|
655 |
// play will use position 0, so it's actually equivalent to curpos == 0
|
642 |
if (curpos == -1)
|
656 |
if (curpos == -1) {
|
643 |
curpos = 0;
|
657 |
curpos = 0;
|
|
|
658 |
}
|
644 |
|
659 |
|
645 |
if (mpds.qlen == 0 && setnext) {
|
660 |
if (mpds.qlen == 0 && setnext) {
|
646 |
LOGDEB("setNextAVTRansportURI invoked but empty queue!" << endl);
|
661 |
LOGDEB("setNextAVTRansportURI invoked but empty queue!" << endl);
|
647 |
return UPNP_E_INVALID_PARAM;
|
662 |
return UPNP_E_INVALID_PARAM;
|
648 |
}
|
663 |
}
|
|
... |
|
... |
972 |
#define OPT_d 0x8
|
987 |
#define OPT_d 0x8
|
973 |
#define OPT_D 0x10
|
988 |
#define OPT_D 0x10
|
974 |
#define OPT_c 0x20
|
989 |
#define OPT_c 0x20
|
975 |
#define OPT_l 0x40
|
990 |
#define OPT_l 0x40
|
976 |
#define OPT_f 0x80
|
991 |
#define OPT_f 0x80
|
|
|
992 |
#define OPT_q 0x100
|
|
|
993 |
|
977 |
static const char usage[] =
|
994 |
static const char usage[] =
|
978 |
"-c configfile \t configuration file to use\n"
|
995 |
"-c configfile \t configuration file to use\n"
|
979 |
"-h host \t specify host MPD is running on\n"
|
996 |
"-h host \t specify host MPD is running on\n"
|
980 |
"-p port \t specify MPD port\n"
|
997 |
"-p port \t specify MPD port\n"
|
981 |
"-d logfilename\t debug messages to\n"
|
998 |
"-d logfilename\t debug messages to\n"
|
982 |
"-l loglevel\t log level (0-6)\n"
|
999 |
"-l loglevel\t log level (0-6)\n"
|
983 |
"-D \t run as a daemon\n"
|
1000 |
"-D \t run as a daemon\n"
|
984 |
"-f friendlyname\t define device displayed name\n"
|
1001 |
"-f friendlyname\t define device displayed name\n"
|
|
|
1002 |
"-q 0|1 \t if set, we own the mpd queue, else avoid clearing it whenever we feel like it"
|
985 |
" \n\n"
|
1003 |
" \n\n"
|
986 |
;
|
1004 |
;
|
987 |
static void
|
1005 |
static void
|
988 |
Usage(void)
|
1006 |
Usage(void)
|
989 |
{
|
1007 |
{
|
|
... |
|
... |
1003 |
// string upnplogfilename("/tmp/upmpd_libupnp.log");
|
1021 |
// string upnplogfilename("/tmp/upmpd_libupnp.log");
|
1004 |
string logfilename;
|
1022 |
string logfilename;
|
1005 |
int loglevel(upnppdebug::Logger::LLINF);
|
1023 |
int loglevel(upnppdebug::Logger::LLINF);
|
1006 |
string configfile;
|
1024 |
string configfile;
|
1007 |
string friendlyname(dfltFriendlyName);
|
1025 |
string friendlyname(dfltFriendlyName);
|
|
|
1026 |
bool ownqueue = true;
|
1008 |
|
1027 |
|
1009 |
const char *cp;
|
1028 |
const char *cp;
|
1010 |
if ((cp = getenv("UPMPD_HOST")))
|
1029 |
if ((cp = getenv("UPMPD_HOST")))
|
1011 |
mpdhost = cp;
|
1030 |
mpdhost = cp;
|
1012 |
if ((cp = getenv("UPMPD_PORT")))
|
1031 |
if ((cp = getenv("UPMPD_PORT")))
|
|
... |
|
... |
1035 |
mpdhost = *(++argv); argc--; goto b1;
|
1054 |
mpdhost = *(++argv); argc--; goto b1;
|
1036 |
case 'l': op_flags |= OPT_l; if (argc < 2) Usage();
|
1055 |
case 'l': op_flags |= OPT_l; if (argc < 2) Usage();
|
1037 |
loglevel = atoi(*(++argv)); argc--; goto b1;
|
1056 |
loglevel = atoi(*(++argv)); argc--; goto b1;
|
1038 |
case 'p': op_flags |= OPT_p; if (argc < 2) Usage();
|
1057 |
case 'p': op_flags |= OPT_p; if (argc < 2) Usage();
|
1039 |
mpdport = atoi(*(++argv)); argc--; goto b1;
|
1058 |
mpdport = atoi(*(++argv)); argc--; goto b1;
|
|
|
1059 |
case 'q': op_flags |= OPT_q; if (argc < 2) Usage();
|
|
|
1060 |
ownqueue = atoi(*(++argv)) != 0; argc--; goto b1;
|
1040 |
default: Usage(); break;
|
1061 |
default: Usage(); break;
|
1041 |
}
|
1062 |
}
|
1042 |
b1: argc--; argv++;
|
1063 |
b1: argc--; argv++;
|
1043 |
}
|
1064 |
}
|
1044 |
|
1065 |
|
|
... |
|
... |
1061 |
if (!(op_flags & OPT_h))
|
1082 |
if (!(op_flags & OPT_h))
|
1062 |
config.get("mpdhost", mpdhost);
|
1083 |
config.get("mpdhost", mpdhost);
|
1063 |
if (!(op_flags & OPT_p) && config.get("mpdport", value)) {
|
1084 |
if (!(op_flags & OPT_p) && config.get("mpdport", value)) {
|
1064 |
mpdport = atoi(value.c_str());
|
1085 |
mpdport = atoi(value.c_str());
|
1065 |
}
|
1086 |
}
|
|
|
1087 |
if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
|
|
|
1088 |
ownqueue = atoi(value.c_str()) != 0;
|
|
|
1089 |
}
|
1066 |
}
|
1090 |
}
|
1067 |
|
1091 |
|
1068 |
if (upnppdebug::Logger::getTheLog(logfilename) == 0) {
|
1092 |
if (upnppdebug::Logger::getTheLog(logfilename) == 0) {
|
1069 |
cerr << "Can't initialize log" << endl;
|
1093 |
cerr << "Can't initialize log" << endl;
|
1070 |
return 1;
|
1094 |
return 1;
|
|
... |
|
... |
1133 |
xmlfiles["description.xml"] = description;
|
1157 |
xmlfiles["description.xml"] = description;
|
1134 |
xmlfiles["RenderingControl.xml"] = rdc_scdp;
|
1158 |
xmlfiles["RenderingControl.xml"] = rdc_scdp;
|
1135 |
xmlfiles["AVTransport.xml"] = avt_scdp;
|
1159 |
xmlfiles["AVTransport.xml"] = avt_scdp;
|
1136 |
|
1160 |
|
1137 |
// Initialize the UPnP device object.
|
1161 |
// Initialize the UPnP device object.
|
1138 |
UpMpd device(string("uuid:") + UUID, xmlfiles, &mpdcli);
|
1162 |
UpMpd device(string("uuid:") + UUID, xmlfiles, &mpdcli,
|
|
|
1163 |
ownqueue?UpMpd::upmpdOwnQueue:UpMpd::upmpdNone);
|
1139 |
|
1164 |
|
1140 |
LOGDEB("Entering event loop" << endl);
|
1165 |
LOGDEB("Entering event loop" << endl);
|
1141 |
|
1166 |
|
1142 |
// And forever generate state change events.
|
1167 |
// And forever generate state change events.
|
1143 |
device.eventloop();
|
1168 |
device.eventloop();
|