--- a/src/main.cxx
+++ b/src/main.cxx
@@ -43,9 +43,9 @@
#include "upmpd.hxx"
#include "mediaserver/mediaserver.hxx"
#include "mediaserver/contentdirectory.hxx"
-#include "httpfs.hxx"
#include "upmpdutils.hxx"
#include "pathut.h"
+#include "readfile.h"
using namespace std;
using namespace UPnPP;
@@ -81,8 +81,8 @@
"-P upport \t specify port number to be used for UPnP\n"
"-O 0|1\t decide if we run and export the OpenHome services\n"
"-v \tprint version info\n"
- "-m <0|1|2|3> media server mode "
- "(default, forked|only renderer|only media|combined)\n"
+ "-m <0|1|2|3|4> media server mode "
+ "(default, forked|only renderer|only media|combined/embedded|combined/multidev)\n"
"\n"
;
@@ -95,18 +95,20 @@
// control points are confused by embedded devices.
//
// - -m 0, default, Forked: this is for the main process, which will
-// implement a Media Renderer device, and, if needed, fork/exec the
-// Media Server (with option -m 2)
+// implement a Media Renderer device, and, if needed, fork/exec the
+// Media Server (with option -m 2)
// - -m 1, RdrOnly: for the main instance: be a Renderer, do not start the
-// Media Server even if the configuration indicates it is needed
-// (this is not used in normal situations, just edit the config
-// instead!)
+// Media Server even if the configuration indicates it is needed
+// (this is not used in normal situations, just edit the config
+// instead!)
// - -m 2, MSOnly Media Server only, this is for the process forked/execed
-// by a main Renderer process, or a standalone Media Server.
+// by a main Renderer process, or a standalone Media Server.
// - -m 3, Combined: for the main process: implement the Media Server
-// as an embedded device. This works just fine with, for example,
-// upplay, but confuses most of the other Control Points.
-enum MSMode {Forked, RdrOnly, MSOnly, Combined};
+// as an embedded device. This works just fine with, for example,
+// upplay, but confuses most of the other Control Points.
+// - -m 4, Combined: for the main process: implement the Media Server
+// as a separate root device. Only works with a modified libupnp.
+enum MSMode {Forked, RdrOnly, MSOnly, CombinedEmbedded, CombinedMultiDev};
static void
versionInfo(FILE *fp)
@@ -150,8 +152,16 @@
}
};
+// Ad-hoc struct to hold the input identification parameters for the device(s)
+struct UDevIds {
+ std::string fname;
+ std::string uuid;
+ std::string fnameMS;
+ std::string uuidMS;
+};
+
// Static for cleanup in sig handler.
-static UpnpDevice *dev;
+static vector<UpnpDevice *> devs;
string g_datadir(DATADIR "/");
string g_cachedir("/var/cache/upmpdcli");
@@ -165,7 +175,9 @@
static void onsig(int)
{
LOGDEB("Got sig" << endl);
- dev->shouldExit();
+ for (auto& dev : devs) {
+ dev->shouldExit();
+ }
}
static const int catchedSigs[] = {SIGINT, SIGQUIT, SIGTERM};
@@ -247,8 +259,9 @@
unsigned short upport = 0;
string upnpip;
int msm = 0;
- bool inprocessms = false;
- bool msonly = false;
+ bool inprocessms{false};
+ bool msonly{false};
+ bool msroot{false};
const char *cp;
if ((cp = getenv("UPMPD_HOST")))
@@ -308,7 +321,7 @@
b1: argc--; argv++;
}
- if (argc != 0 || msm < 0 || msm > 3) {
+ if (argc != 0 || msm < 0 || msm > 4) {
Usage();
}
MSMode arg_msmode = MSMode(msm);
@@ -442,9 +455,15 @@
inprocessms = true;
msonly = true;
break;
- case Combined:
+ case CombinedEmbedded:
inprocessms = true;
msonly = false;
+ msroot = false;
+ break;
+ case CombinedMultiDev:
+ inprocessms = true;
+ msonly = false;
+ msroot = true;
break;
case RdrOnly:
case Forked:
@@ -690,15 +709,8 @@
pidfilename = pidfilename + "-ms";
}
- // Initialize the data we serve through HTTP (device and service
- // descriptions, icons, presentation page, etc.)
- unordered_map<string, VDirContent> files;
- if (!initHttpFs(files, g_datadir, ids, enableAV, enableOH,
- !senderpath.empty(), inprocessms, msonly,
- iconpath, presentationhtml)) {
- exit(1);
- }
-
+ opts.iconpath = iconpath;
+ opts.presentationhtml = presentationhtml;
if (ownqueue)
opts.options |= UpMpd::upmpdOwnQueue;
if (enableOH)
@@ -734,48 +746,49 @@
if (!enableAV)
opts.options |= UpMpd::upmpdNoAV;
+ setupsigs();
+
UpMpd *mediarenderer{nullptr};
if (!msonly) {
mediarenderer = new UpMpd(string("uuid:") + ids.uuid, ids.fname,
- ohProductDesc, files, mpdclip, opts);
- }
-
- MediaServer *mediaserver = nullptr;
- unordered_map<string, VDirContent> emptyfiles =
- unordered_map<string, VDirContent>();
- unordered_map<string, VDirContent> *msfiles = &emptyfiles;
+ ohProductDesc, mpdclip, opts);
+ devs.push_back(mediarenderer);
+ }
+
+ MediaServer *mediaserver{nullptr};
if (inprocessms) {
- if (msonly) {
- msfiles = &files;
- }
- // Create the Media Server embedded device object. There needs
- // be no reference to the root object because there can be
- // only one (libupnp restriction)
- mediaserver = new MediaServer(string("uuid:") + ids.uuidMS, ids.fnameMS,
- enableMediaServer, *msfiles);
+ // Create the Media Server device. If msonly is set, both
+ // branches do the same thing and create a root device
+ // (mediarenderer is null).
+ // The multidev modified libupnp is needed for using 2 root devices
+ mediaserver = new MediaServer(msroot?nullptr:mediarenderer, string("uuid:") +
+ ids.uuidMS,ids.fnameMS, enableMediaServer);
+ devs.push_back(mediaserver);
+ LOGDEB("Media server event loop" << endl);
} else if (enableMediaServer) {
startMsOnlyProcess();
}
-
- // And forever generate state change events.
- LOGDEB("Entering event loop" << endl);
- setupsigs();
- if (msonly) {
- assert(nullptr != mediaserver);
- dev = mediaserver;
- LOGDEB("Media server event loop" << endl);
- if (enableMediaServer) {
- mediaserver->eventloop();
- } else {
- pause();
- }
- } else {
+ if (!msonly) {
LOGDEB("Renderer event loop" << endl);
- assert(nullptr != mediarenderer);
- dev = mediarenderer;
- mediarenderer->eventloop();
- }
+ mediarenderer->startloop();
+ }
+ if (inprocessms && enableMediaServer) {
+ mediaserver->startloop();
+ }
+ pause();
LOGDEB("Event loop returned" << endl);
return 0;
}
+
+// Read file from datadir
+bool readLibFile(const std::string& name, std::string& contents)
+{
+ string path = path_cat(g_datadir, name);
+ string reason;
+ if (!file_to_string(path, contents, &reason)) {
+ LOGERR("readLibFile: error reading " << name << " : " << reason << endl);
+ return false;
+ }
+ return true;
+}