|
a/src/main.cxx |
|
b/src/main.cxx |
|
... |
|
... |
49 |
using namespace UPnPP;
|
49 |
using namespace UPnPP;
|
50 |
|
50 |
|
51 |
static char *thisprog;
|
51 |
static char *thisprog;
|
52 |
|
52 |
|
53 |
static int op_flags;
|
53 |
static int op_flags;
|
54 |
#define OPT_MOINS 0x1
|
54 |
#define OPT_MOINS 0x1
|
55 |
#define OPT_h 0x2
|
55 |
#define OPT_D 0x2
|
56 |
#define OPT_p 0x4
|
56 |
#define OPT_O 0x4
|
57 |
#define OPT_d 0x8
|
57 |
#define OPT_P 0x8
|
58 |
#define OPT_D 0x10
|
58 |
#define OPT_c 0x10
|
59 |
#define OPT_c 0x20
|
59 |
#define OPT_d 0x20
|
60 |
#define OPT_l 0x40
|
60 |
#define OPT_f 0x40
|
61 |
#define OPT_f 0x80
|
61 |
#define OPT_h 0x80
|
|
|
62 |
#define OPT_i 0x100
|
|
|
63 |
#define OPT_l 0x200
|
|
|
64 |
#define OPT_m 0x400
|
|
|
65 |
#define OPT_p 0x800
|
62 |
#define OPT_q 0x100
|
66 |
#define OPT_q 0x1000
|
63 |
#define OPT_i 0x200
|
|
|
64 |
#define OPT_P 0x400
|
|
|
65 |
#define OPT_O 0x800
|
|
|
66 |
#define OPT_v 0x1000
|
67 |
#define OPT_v 0x2000
|
67 |
|
68 |
|
68 |
static const char usage[] =
|
69 |
static const char usage[] =
|
69 |
"-c configfile \t configuration file to use\n"
|
70 |
"-c configfile \t configuration file to use\n"
|
70 |
"-h host \t specify host MPD is running on\n"
|
71 |
"-h host \t specify host MPD is running on\n"
|
71 |
"-p port \t specify MPD port\n"
|
72 |
"-p port \t specify MPD port\n"
|
|
... |
|
... |
76 |
"-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
|
77 |
"-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
|
77 |
"-i iface \t specify network interface name to be used for UPnP\n"
|
78 |
"-i iface \t specify network interface name to be used for UPnP\n"
|
78 |
"-P upport \t specify port number to be used for UPnP\n"
|
79 |
"-P upport \t specify port number to be used for UPnP\n"
|
79 |
"-O 0|1\t decide if we run and export the OpenHome services\n"
|
80 |
"-O 0|1\t decide if we run and export the OpenHome services\n"
|
80 |
"-v \tprint version info\n"
|
81 |
"-v \tprint version info\n"
|
|
|
82 |
"-m <0|1|2|3> media server mode "
|
|
|
83 |
"(default, forked|only renderer|only media|combined)\n"
|
81 |
"\n"
|
84 |
"\n"
|
82 |
;
|
85 |
;
|
|
|
86 |
|
|
|
87 |
enum MSMode {Forked, RdrOnly, MSOnly, Combined};
|
83 |
|
88 |
|
84 |
static void
|
89 |
static void
|
85 |
versionInfo(FILE *fp)
|
90 |
versionInfo(FILE *fp)
|
86 |
{
|
91 |
{
|
87 |
fprintf(fp, "Upmpdcli %s %s\n",
|
92 |
fprintf(fp, "Upmpdcli %s %s\n",
|
|
... |
|
... |
160 |
}
|
165 |
}
|
161 |
}
|
166 |
}
|
162 |
|
167 |
|
163 |
int main(int argc, char *argv[])
|
168 |
int main(int argc, char *argv[])
|
164 |
{
|
169 |
{
|
|
|
170 |
vector<string> savedargs;
|
|
|
171 |
for (int i = 0; i < argc; i++) {
|
|
|
172 |
savedargs.push_back(argv[i]);
|
|
|
173 |
}
|
|
|
174 |
|
165 |
// Path for the sc2mpd command, or empty
|
175 |
// Path for the sc2mpd command, or empty
|
166 |
string sc2mpdpath;
|
176 |
string sc2mpdpath;
|
167 |
|
177 |
|
168 |
// Sender mode: path for the command creating the mpd and mpd2sc
|
178 |
// Sender mode: path for the command creating the mpd and mpd2sc
|
169 |
// processes, and port for the auxiliary mpd.
|
179 |
// processes, and port for the auxiliary mpd.
|
|
... |
|
... |
188 |
string iconpath(DATADIR "/icon.png");
|
198 |
string iconpath(DATADIR "/icon.png");
|
189 |
string presentationhtml(DATADIR "/presentation.html");
|
199 |
string presentationhtml(DATADIR "/presentation.html");
|
190 |
string iface;
|
200 |
string iface;
|
191 |
unsigned short upport = 0;
|
201 |
unsigned short upport = 0;
|
192 |
string upnpip;
|
202 |
string upnpip;
|
193 |
|
203 |
int msm = 0;
|
|
|
204 |
bool inprocessms = false;
|
|
|
205 |
bool msonly = false;
|
|
|
206 |
|
194 |
const char *cp;
|
207 |
const char *cp;
|
195 |
if ((cp = getenv("UPMPD_HOST")))
|
208 |
if ((cp = getenv("UPMPD_HOST")))
|
196 |
mpdhost = cp;
|
209 |
mpdhost = cp;
|
197 |
if ((cp = getenv("UPMPD_PORT")))
|
210 |
if ((cp = getenv("UPMPD_PORT")))
|
198 |
mpdport = atoi(cp);
|
211 |
mpdport = atoi(cp);
|
|
... |
|
... |
224 |
mpdhost = *(++argv); argc--; goto b1;
|
237 |
mpdhost = *(++argv); argc--; goto b1;
|
225 |
case 'i': op_flags |= OPT_i; if (argc < 2) Usage();
|
238 |
case 'i': op_flags |= OPT_i; if (argc < 2) Usage();
|
226 |
iface = *(++argv); argc--; goto b1;
|
239 |
iface = *(++argv); argc--; goto b1;
|
227 |
case 'l': op_flags |= OPT_l; if (argc < 2) Usage();
|
240 |
case 'l': op_flags |= OPT_l; if (argc < 2) Usage();
|
228 |
loglevel = atoi(*(++argv)); argc--; goto b1;
|
241 |
loglevel = atoi(*(++argv)); argc--; goto b1;
|
|
|
242 |
case 'm': op_flags |= OPT_m; if (argc < 2) Usage();
|
|
|
243 |
msm = atoi(*(++argv)); argc--; goto b1;
|
229 |
case 'O': {
|
244 |
case 'O': {
|
230 |
op_flags |= OPT_O;
|
245 |
op_flags |= OPT_O;
|
231 |
if (argc < 2) Usage();
|
246 |
if (argc < 2) Usage();
|
232 |
const char *cp = *(++argv);
|
247 |
const char *cp = *(++argv);
|
233 |
if (*cp == '1' || *cp == 't' || *cp == 'T' || *cp == 'y' ||
|
248 |
if (*cp == '1' || *cp == 't' || *cp == 'T' || *cp == 'y' ||
|
|
... |
|
... |
245 |
default: Usage(); break;
|
260 |
default: Usage(); break;
|
246 |
}
|
261 |
}
|
247 |
b1: argc--; argv++;
|
262 |
b1: argc--; argv++;
|
248 |
}
|
263 |
}
|
249 |
|
264 |
|
250 |
if (argc != 0)
|
265 |
if (argc != 0 || msm < 0 || msm > 3) {
|
251 |
Usage();
|
266 |
Usage();
|
252 |
|
267 |
}
|
|
|
268 |
MSMode msmode = MSMode(msm);
|
|
|
269 |
|
253 |
UpMpd::Options opts;
|
270 |
UpMpd::Options opts;
|
254 |
|
271 |
|
255 |
string cachedir;
|
272 |
string cachedir;
|
256 |
if (!g_configfilename.empty()) {
|
273 |
if (!g_configfilename.empty()) {
|
257 |
g_config = new ConfSimple(g_configfilename.c_str(), 1, true);
|
274 |
g_config = new ConfSimple(g_configfilename.c_str(), 1, true);
|
|
... |
|
... |
344 |
|
361 |
|
345 |
g_config->get("scsenderpath", senderpath);
|
362 |
g_config->get("scsenderpath", senderpath);
|
346 |
if (g_config->get("scsendermpdport", value))
|
363 |
if (g_config->get("scsendermpdport", value))
|
347 |
sendermpdport = atoi(value.c_str());
|
364 |
sendermpdport = atoi(value.c_str());
|
348 |
|
365 |
|
|
|
366 |
// If a streaming service is enabled (only tidal for now), we
|
|
|
367 |
// need a Media Server. The way we implement it depends on the
|
|
|
368 |
// command line option:
|
349 |
if (g_config->hasNameAnywhere("tidaluser")) {
|
369 |
if (g_config->hasNameAnywhere("tidaluser")) {
|
350 |
enableMediaServer = true;
|
370 |
enableMediaServer = true;
|
|
|
371 |
switch (msmode) {
|
|
|
372 |
case MSOnly:
|
|
|
373 |
inprocessms = true;
|
|
|
374 |
msonly = true;
|
|
|
375 |
break;
|
|
|
376 |
case Combined:
|
|
|
377 |
inprocessms = true;
|
|
|
378 |
msonly = false;
|
|
|
379 |
break;
|
|
|
380 |
case RdrOnly:
|
|
|
381 |
case Forked:
|
|
|
382 |
default:
|
|
|
383 |
inprocessms = false;
|
|
|
384 |
msonly = false;
|
|
|
385 |
break;
|
|
|
386 |
}
|
351 |
}
|
387 |
}
|
352 |
}
|
388 |
}
|
353 |
if (Logger::getTheLog(logfilename) == 0) {
|
389 |
if (Logger::getTheLog(logfilename) == 0) {
|
354 |
cerr << "Can't initialize log" << endl;
|
390 |
cerr << "Can't initialize log" << endl;
|
355 |
return 1;
|
391 |
return 1;
|
|
... |
|
... |
548 |
}
|
584 |
}
|
549 |
|
585 |
|
550 |
// Create unique ID
|
586 |
// Create unique ID
|
551 |
string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
|
587 |
string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
|
552 |
|
588 |
|
|
|
589 |
// If running as mediaserver only, make sure we don't conflict
|
|
|
590 |
// with a possible renderer
|
|
|
591 |
if (msonly) {
|
|
|
592 |
pidfilename = pidfilename + "-ms";
|
|
|
593 |
}
|
|
|
594 |
|
553 |
// Initialize the data we serve through HTTP (device and service
|
595 |
// Initialize the data we serve through HTTP (device and service
|
554 |
// descriptions, icons, presentation page, etc.)
|
596 |
// descriptions, icons, presentation page, etc.)
|
555 |
unordered_map<string, VDirContent> files;
|
597 |
unordered_map<string, VDirContent> files;
|
556 |
if (!initHttpFs(files, g_datadir, UUID, friendlyname, enableAV, enableOH,
|
598 |
if (!initHttpFs(files, g_datadir, UUID, friendlyname, enableAV, enableOH,
|
557 |
!senderpath.empty(), enableL16, enableMediaServer,
|
599 |
!senderpath.empty(), enableL16, inprocessms,
|
558 |
iconpath, presentationhtml)) {
|
600 |
msonly, iconpath, presentationhtml)) {
|
559 |
exit(1);
|
601 |
exit(1);
|
560 |
}
|
602 |
}
|
561 |
|
603 |
|
562 |
if (ownqueue)
|
604 |
if (ownqueue)
|
563 |
opts.options |= UpMpd::upmpdOwnQueue;
|
605 |
opts.options |= UpMpd::upmpdOwnQueue;
|
|
... |
|
... |
577 |
|
619 |
|
578 |
if (!enableAV)
|
620 |
if (!enableAV)
|
579 |
opts.options |= UpMpd::upmpdNoAV;
|
621 |
opts.options |= UpMpd::upmpdNoAV;
|
580 |
|
622 |
|
581 |
// Initialize the UPnP root device object.
|
623 |
// Initialize the UPnP root device object.
|
582 |
UpMpd device(string("uuid:") + UUID, friendlyname, ohProductDesc,
|
624 |
UpMpd *mediarenderer = nullptr;
|
583 |
files, mpdclip, opts);
|
625 |
if (!msonly) {
|
584 |
dev = &device;
|
626 |
mediarenderer = new UpMpd(string("uuid:") + UUID, friendlyname,
|
|
|
627 |
ohProductDesc, files, mpdclip, opts);
|
|
|
628 |
}
|
585 |
|
629 |
|
586 |
MediaServer *devmedia = nullptr;
|
630 |
MediaServer *mediaserver = nullptr;
|
587 |
if (enableMediaServer) {
|
631 |
unordered_map<string, VDirContent> emptyfiles =
|
|
|
632 |
unordered_map<string, VDirContent>();
|
|
|
633 |
unordered_map<string, VDirContent> *msfiles = &emptyfiles;
|
|
|
634 |
vector<string> args{"-m", "2"};
|
|
|
635 |
ExecCmd cmd;
|
|
|
636 |
|
|
|
637 |
if (inprocessms) {
|
|
|
638 |
if (op_flags & OPT_m) {
|
|
|
639 |
msfiles = &files;
|
|
|
640 |
}
|
588 |
// Create the Media Server embedded device object. There needs
|
641 |
// Create the Media Server embedded device object. There needs
|
589 |
// be no reference to the root object because there can be
|
642 |
// be no reference to the root object because there can be
|
590 |
// only one (libupnp restriction)
|
643 |
// only one (libupnp restriction)
|
591 |
devmedia = new MediaServer(string("uuid:") + uuidMediaServer(UUID),
|
644 |
mediaserver = new MediaServer(string("uuid:") + uuidMediaServer(UUID),
|
592 |
friendlyNameMediaServer(friendlyname));
|
645 |
friendlyNameMediaServer(friendlyname),
|
|
|
646 |
*msfiles);
|
|
|
647 |
} else if (enableMediaServer) {
|
|
|
648 |
// Fork process for media server, replacing whatever -m option
|
|
|
649 |
// was given with -m 2 (ms only)
|
|
|
650 |
string cmdpath(savedargs[0]);
|
|
|
651 |
for (unsigned int i = 1; i < savedargs.size(); i++) {
|
|
|
652 |
string sa(savedargs[i]);
|
|
|
653 |
if (sa[sa.length()-1] == 'm') {
|
|
|
654 |
sa = sa.substr(0, sa.length()-1);
|
|
|
655 |
if (int(i) == argc -1)
|
|
|
656 |
Usage();
|
|
|
657 |
i++;
|
|
|
658 |
}
|
|
|
659 |
if (!sa.empty() && sa.compare("-")) {
|
|
|
660 |
args.push_back(sa);
|
|
|
661 |
}
|
593 |
}
|
662 |
}
|
|
|
663 |
cmd.startExec(cmdpath, args, false, false);
|
|
|
664 |
}
|
594 |
UPMPD_UNUSED(devmedia);
|
665 |
UPMPD_UNUSED(mediaserver);
|
595 |
|
666 |
|
596 |
// And forever generate state change events.
|
667 |
// And forever generate state change events.
|
597 |
LOGDEB("Entering event loop" << endl);
|
668 |
LOGDEB("Entering event loop" << endl);
|
598 |
setupsigs();
|
669 |
setupsigs();
|
599 |
device.eventloop();
|
670 |
if (msonly) {
|
|
|
671 |
dev = mediaserver;
|
|
|
672 |
LOGDEB("Media server event loop" << endl);
|
|
|
673 |
mediaserver->eventloop();
|
|
|
674 |
} else {
|
|
|
675 |
LOGDEB("Renderer event loop" << endl);
|
|
|
676 |
dev = mediarenderer;
|
|
|
677 |
mediarenderer->eventloop();
|
|
|
678 |
}
|
600 |
LOGDEB("Event loop returned" << endl);
|
679 |
LOGDEB("Event loop returned" << endl);
|
601 |
|
|
|
602 |
return 0;
|
680 |
return 0;
|
603 |
}
|
681 |
}
|