|
a/upmpd/upmpd.cxx |
|
b/upmpd/upmpd.cxx |
|
... |
|
... |
51 |
|
51 |
|
52 |
// Note: if we ever need this to work without cxx11, there is this:
|
52 |
// Note: if we ever need this to work without cxx11, there is this:
|
53 |
// http://www.tutok.sk/fastgl/callback.html
|
53 |
// http://www.tutok.sk/fastgl/callback.html
|
54 |
UpMpd::UpMpd(const string& deviceid,
|
54 |
UpMpd::UpMpd(const string& deviceid,
|
55 |
const unordered_map<string, string>& xmlfiles,
|
55 |
const unordered_map<string, string>& xmlfiles,
|
56 |
MPDCli *mpdcli, Options opts)
|
56 |
MPDCli *mpdcli, unsigned int opts)
|
57 |
: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_mpds(0),
|
57 |
: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_mpds(0),
|
58 |
m_options(opts)
|
58 |
m_options(opts)
|
59 |
{
|
59 |
{
|
60 |
// Note: the order is significant here as it will be used when
|
60 |
// Note: the order is significant here as it will be used when
|
61 |
// calling the getStatus() methods, and we want AVTransport to
|
61 |
// calling the getStatus() methods, and we want AVTransport to
|
62 |
// update the mpd status for OHInfo
|
62 |
// update the mpd status for OHInfo
|
63 |
UpMpdRenderCtl *rdctl = new UpMpdRenderCtl(this);
|
63 |
UpMpdRenderCtl *rdctl = new UpMpdRenderCtl(this);
|
64 |
m_services.push_back(rdctl);
|
64 |
m_services.push_back(rdctl);
|
65 |
m_services.push_back(new UpMpdAVTransport(this));
|
65 |
m_services.push_back(new UpMpdAVTransport(this));
|
66 |
m_services.push_back(new UpMpdConMan(this));
|
66 |
m_services.push_back(new UpMpdConMan(this));
|
|
|
67 |
if (m_options & upmpdDoOH) {
|
67 |
m_services.push_back(new OHProduct(this));
|
68 |
m_services.push_back(new OHProduct(this));
|
68 |
m_services.push_back(new OHInfo(this));
|
69 |
m_services.push_back(new OHInfo(this));
|
69 |
m_services.push_back(new OHTime(this));
|
70 |
m_services.push_back(new OHTime(this));
|
70 |
m_services.push_back(new OHVolume(this, rdctl));
|
71 |
m_services.push_back(new OHVolume(this, rdctl));
|
71 |
m_services.push_back(new OHPlaylist(this, rdctl));
|
72 |
m_services.push_back(new OHPlaylist(this, rdctl));
|
|
|
73 |
}
|
72 |
}
|
74 |
}
|
73 |
|
75 |
|
74 |
UpMpd::~UpMpd()
|
76 |
UpMpd::~UpMpd()
|
75 |
{
|
77 |
{
|
76 |
for (vector<UpnpService*>::iterator it = m_services.begin();
|
78 |
for (vector<UpnpService*>::iterator it = m_services.begin();
|
|
... |
|
... |
105 |
#define OPT_i 0x200
|
107 |
#define OPT_i 0x200
|
106 |
#define OPT_P 0x400
|
108 |
#define OPT_P 0x400
|
107 |
|
109 |
|
108 |
static const char usage[] =
|
110 |
static const char usage[] =
|
109 |
"-c configfile \t configuration file to use\n"
|
111 |
"-c configfile \t configuration file to use\n"
|
110 |
"-h host \t specify host MPD is running on\n"
|
112 |
"""""""""""""""""""""""""""""-h host \t specify host MPD is running on\n"""""""""""""""""""""""""""""
|
111 |
"-p port \t specify MPD port\n"
|
113 |
"-p port \t specify MPD port\n"
|
112 |
"-d logfilename\t debug messages to\n"
|
114 |
"-d logfilename\t debug messages to\n"
|
113 |
"-l loglevel\t log level (0-6)\n"
|
115 |
"-l loglevel\t log level (0-6)\n"
|
114 |
"-D \t run as a daemon\n"
|
116 |
"-D \t run as a daemon\n"
|
115 |
"-f friendlyname\t define device displayed name\n"
|
117 |
"-f friendlyname\t define device displayed name\n"
|
116 |
"-q 0|1 \t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
|
118 |
"-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
|
117 |
"-i iface \t specify network interface name to be used for UPnP"
|
119 |
"-i iface \t specify network interface name to be used for UPnP"
|
118 |
"-P upport \t specify port number to be used for UPnP"
|
120 |
"-P upport \t specify port number to be used for UPnP"
|
119 |
" \n\n"
|
121 |
" \n\n"
|
120 |
;
|
122 |
;
|
121 |
static void
|
123 |
static void
|
|
... |
|
... |
129 |
|
131 |
|
130 |
static string datadir(DATADIR "/");
|
132 |
static string datadir(DATADIR "/");
|
131 |
static string configdir(CONFIGDIR "/");
|
133 |
static string configdir(CONFIGDIR "/");
|
132 |
|
134 |
|
133 |
// Our XML description data. !Keep description.xml first!
|
135 |
// Our XML description data. !Keep description.xml first!
|
134 |
static const char *xmlfilenames[] =
|
136 |
static vector<const char *> xmlfilenames =
|
|
|
137 |
{
|
135 |
{/* keep first */ "description.xml",
|
138 |
/* keep first */ "description.xml", /* keep first */
|
136 |
"RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml",
|
139 |
"RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml",
|
137 |
"OHProduct.xml", "OHInfo.xml", "OHTime.xml", "OHVolume.xml",
|
|
|
138 |
"OHPlaylist.xml",
|
|
|
139 |
};
|
140 |
};
|
|
|
141 |
static vector<const char *> ohxmlfilenames =
|
|
|
142 |
{
|
|
|
143 |
"OHProduct.xml", "OHInfo.xml", "OHTime.xml", "OHVolume.xml",
|
|
|
144 |
"OHPlaylist.xml",
|
|
|
145 |
};
|
140 |
|
146 |
|
141 |
static const int xmlfilenamescnt = sizeof(xmlfilenames) / sizeof(char *);
|
147 |
|
|
|
148 |
static const string ohDesc(
|
|
|
149 |
"<service>"
|
|
|
150 |
" <serviceType>urn:av-openhome-org:service:Product:1</serviceType>"
|
|
|
151 |
" <serviceId>urn:av-openhome-org:serviceId:Product</serviceId>"
|
|
|
152 |
" <SCPDURL>/OHProduct.xml</SCPDURL>"
|
|
|
153 |
" <controlURL>/ctl/OHProduct</controlURL>"
|
|
|
154 |
" <eventSubURL>/evt/OHProduct</eventSubURL>"
|
|
|
155 |
"</service>"
|
|
|
156 |
"<service>"
|
|
|
157 |
" <serviceType>urn:av-openhome-org:service:Info:1</serviceType>"
|
|
|
158 |
" <serviceId>urn:av-openhome-org:serviceId:Info</serviceId>"
|
|
|
159 |
" <SCPDURL>/OHInfo.xml</SCPDURL>"
|
|
|
160 |
" <controlURL>/ctl/OHInfo</controlURL>"
|
|
|
161 |
" <eventSubURL>/evt/OHInfo</eventSubURL>"
|
|
|
162 |
"</service>"
|
|
|
163 |
"<service>"
|
|
|
164 |
" <serviceType>urn:av-openhome-org:service:Time:1</serviceType>"
|
|
|
165 |
" <serviceId>urn:av-openhome-org:serviceId:Time</serviceId>"
|
|
|
166 |
" <SCPDURL>/OHTime.xml</SCPDURL>"
|
|
|
167 |
" <controlURL>/ctl/OHTime</controlURL>"
|
|
|
168 |
" <eventSubURL>/evt/OHTime</eventSubURL>"
|
|
|
169 |
"</service>"
|
|
|
170 |
"<service>"
|
|
|
171 |
" <serviceType>urn:av-openhome-org:service:Volume:1</serviceType>"
|
|
|
172 |
" <serviceId>urn:av-openhome-org:serviceId:Volume</serviceId>"
|
|
|
173 |
" <SCPDURL>/OHVolume.xml</SCPDURL>"
|
|
|
174 |
" <controlURL>/ctl/OHVolume</controlURL>"
|
|
|
175 |
" <eventSubURL>/evt/OHVolume</eventSubURL>"
|
|
|
176 |
"</service>"
|
|
|
177 |
"<service>"
|
|
|
178 |
" <serviceType>urn:av-openhome-org:service:Playlist:1</serviceType>"
|
|
|
179 |
" <serviceId>urn:av-openhome-org:serviceId:Playlist</serviceId>"
|
|
|
180 |
" <SCPDURL>/OHPlaylist.xml</SCPDURL>"
|
|
|
181 |
" <controlURL>/ctl/OHPlaylist</controlURL>"
|
|
|
182 |
" <eventSubURL>/evt/OHPlaylist</eventSubURL>"
|
|
|
183 |
"</service>"
|
|
|
184 |
);
|
142 |
|
185 |
|
143 |
int main(int argc, char *argv[])
|
186 |
int main(int argc, char *argv[])
|
144 |
{
|
187 |
{
|
145 |
string mpdhost("localhost");
|
188 |
string mpdhost("localhost");
|
146 |
int mpdport = 6600;
|
189 |
int mpdport = 6600;
|
|
... |
|
... |
148 |
string logfilename;
|
191 |
string logfilename;
|
149 |
int loglevel(upnppdebug::Logger::LLINF);
|
192 |
int loglevel(upnppdebug::Logger::LLINF);
|
150 |
string configfile;
|
193 |
string configfile;
|
151 |
string friendlyname(dfltFriendlyName);
|
194 |
string friendlyname(dfltFriendlyName);
|
152 |
bool ownqueue = true;
|
195 |
bool ownqueue = true;
|
|
|
196 |
bool openhome = false;
|
153 |
string upmpdcliuser("upmpdcli");
|
197 |
string upmpdcliuser("upmpdcli");
|
154 |
string pidfilename("/var/run/upmpdcli.pid");
|
198 |
string pidfilename("/var/run/upmpdcli.pid");
|
155 |
string iface;
|
199 |
string iface;
|
156 |
unsigned short upport = 0;
|
200 |
unsigned short upport = 0;
|
157 |
string upnpip;
|
201 |
string upnpip;
|
|
... |
|
... |
225 |
}
|
269 |
}
|
226 |
config.get("mpdpassword", mpdpassword);
|
270 |
config.get("mpdpassword", mpdpassword);
|
227 |
if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
|
271 |
if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
|
228 |
ownqueue = atoi(value.c_str()) != 0;
|
272 |
ownqueue = atoi(value.c_str()) != 0;
|
229 |
}
|
273 |
}
|
|
|
274 |
if (config.get("openhome", value)) {
|
|
|
275 |
openhome = atoi(value.c_str()) != 0;
|
|
|
276 |
}
|
230 |
if (!(op_flags & OPT_i)) {
|
277 |
if (!(op_flags & OPT_i)) {
|
231 |
config.get("upnpiface", iface);
|
278 |
config.get("upnpiface", iface);
|
232 |
if (iface.empty()) {
|
279 |
if (iface.empty()) {
|
233 |
config.get("upnpip", upnpip);
|
280 |
config.get("upnpip", upnpip);
|
234 |
}
|
281 |
}
|
|
... |
|
... |
324 |
|
371 |
|
325 |
// Create unique ID
|
372 |
// Create unique ID
|
326 |
string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
|
373 |
string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
|
327 |
|
374 |
|
328 |
// Read our XML data to make it available from the virtual directory
|
375 |
// Read our XML data to make it available from the virtual directory
|
|
|
376 |
if (openhome) {
|
|
|
377 |
xmlfilenames.insert(xmlfilenames.end(), ohxmlfilenames.begin(),
|
|
|
378 |
ohxmlfilenames.end());
|
|
|
379 |
}
|
329 |
string reason;
|
380 |
string reason;
|
330 |
unordered_map<string, string> xmlfiles;
|
381 |
unordered_map<string, string> xmlfiles;
|
331 |
for (int i = 0; i < xmlfilenamescnt; i++) {
|
382 |
for (unsigned int i = 0; i < xmlfilenames.size(); i++) {
|
332 |
string filename = path_cat(datadir, xmlfilenames[i]);
|
383 |
string filename = path_cat(datadir, xmlfilenames[i]);
|
333 |
string data;
|
384 |
string data;
|
334 |
if (!file_to_string(filename, data, &reason)) {
|
385 |
if (!file_to_string(filename, data, &reason)) {
|
335 |
LOGFAT("Failed reading " << filename << " : " << reason << endl);
|
386 |
LOGFAT("Failed reading " << filename << " : " << reason << endl);
|
336 |
return 1;
|
387 |
return 1;
|
337 |
}
|
388 |
}
|
338 |
if (i == 0) {
|
389 |
if (i == 0) {
|
339 |
// Special for description: set UUID and friendlyname
|
390 |
// Special for description: set UUID and friendlyname
|
340 |
data = regsub1("@UUID@", data, UUID);
|
391 |
data = regsub1("@UUID@", data, UUID);
|
341 |
data = regsub1("@FRIENDLYNAME@", data, friendlyname);
|
392 |
data = regsub1("@FRIENDLYNAME@", data, friendlyname);
|
|
|
393 |
if (openhome)
|
|
|
394 |
data = regsub1("@OPENHOME@", data, ohDesc);
|
342 |
}
|
395 |
}
|
343 |
xmlfiles[xmlfilenames[i]] = data;
|
396 |
xmlfiles[xmlfilenames[i]] = data;
|
344 |
}
|
397 |
}
|
345 |
|
398 |
unsigned int options = UpMpd::upmpdNone;
|
|
|
399 |
if (ownqueue)
|
|
|
400 |
options |= UpMpd::upmpdOwnQueue;
|
|
|
401 |
if (openhome)
|
|
|
402 |
options |= UpMpd::upmpdDoOH;
|
346 |
// Initialize the UPnP device object.
|
403 |
// Initialize the UPnP device object.
|
347 |
UpMpd device(string("uuid:") + UUID, xmlfiles, &mpdcli,
|
404 |
UpMpd device(string("uuid:") + UUID, xmlfiles, &mpdcli, options);
|
348 |
ownqueue ? UpMpd::upmpdOwnQueue : UpMpd::upmpdNone);
|
|
|
349 |
|
405 |
|
350 |
// And forever generate state change events.
|
406 |
// And forever generate state change events.
|
351 |
LOGDEB("Entering event loop" << endl);
|
407 |
LOGDEB("Entering event loop" << endl);
|
352 |
device.eventloop();
|
408 |
device.eventloop();
|
353 |
|
409 |
|