Switch to unified view

a/upmpd/upmpd.cxx b/upmpd/upmpd.cxx
...
...
36
#include "mpdcli.hxx"
36
#include "mpdcli.hxx"
37
#include "upmpdutils.hxx"
37
#include "upmpdutils.hxx"
38
38
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 3 services
42
class UpMpd : public UpnpDevice {
42
class UpMpd : public UpnpDevice {
43
public:
43
public:
44
    enum Options {
44
    enum Options {
45
        upmpdNone,
45
        upmpdNone,
46
        upmpdOwnQueue, // The MPD belongs to us, we shall clear it as we like
46
        upmpdOwnQueue, // The MPD belongs to us, we shall clear it as we like
...
...
74
    int seqcontrol(const SoapArgs& sc, SoapData& data, int what);
74
    int seqcontrol(const SoapArgs& sc, SoapData& data, int what);
75
    virtual bool getEventDataTransport(bool all, 
75
    virtual bool getEventDataTransport(bool all, 
76
                                       std::vector<std::string>& names, 
76
                                       std::vector<std::string>& names, 
77
                                       std::vector<std::string>& values);
77
                                       std::vector<std::string>& values);
78
78
79
  // Connection Manager
80
  int getCurrentConnectionIDs(const SoapArgs& sc, SoapData& data);
81
  int getCurrentConnectionInfo(const SoapArgs& sc, SoapData& data);
82
  int getProtocolInfo(const SoapArgs& sc, SoapData& data);
83
  virtual bool getEventDataCM(bool all, std::vector<std::string>& names, 
84
                              std::vector<std::string>& values);
85
79
    // Re-implemented from the base class and shared by both services
86
    // Re-implemented from the base class and shared by all services
80
    virtual bool getEventData(bool all, const string& serviceid, 
87
    virtual bool getEventData(bool all, const string& serviceid, 
81
                              std::vector<std::string>& names, 
88
                              std::vector<std::string>& names, 
82
                              std::vector<std::string>& values);
89
                              std::vector<std::string>& values);
83
90
84
private:
91
private:
85
    MPDCli *m_mpdcli;
92
    MPDCli *m_mpdcli;
93
94
  string m_curMetadata;
95
  string m_nextMetadata;
86
96
87
    // State variable storage
97
    // State variable storage
88
    unordered_map<string, string> m_rdstate;
98
    unordered_map<string, string> m_rdstate;
89
    unordered_map<string, string> m_tpstate;
99
    unordered_map<string, string> m_tpstate;
90
100
...
...
103
    int m_options;
113
    int m_options;
104
};
114
};
105
115
106
static const string serviceIdRender("urn:upnp-org:serviceId:RenderingControl");
116
static const string serviceIdRender("urn:upnp-org:serviceId:RenderingControl");
107
static const string serviceIdTransport("urn:upnp-org:serviceId:AVTransport");
117
static const string serviceIdTransport("urn:upnp-org:serviceId:AVTransport");
118
static const string serviceIdCM("urn:upnp-org:serviceId:ConnectionManager");
108
119
109
UpMpd::UpMpd(const string& deviceid, 
120
UpMpd::UpMpd(const string& deviceid, 
110
             const unordered_map<string, string>& xmlfiles,
121
             const unordered_map<string, string>& xmlfiles,
111
             MPDCli *mpdcli, Options opts)
122
             MPDCli *mpdcli, Options opts)
112
    : UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_desiredvolume(-1),
123
    : UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_desiredvolume(-1),
...
...
187
    {   auto bound = bind(&UpMpd::seqcontrol, this, _1, _2, 0);
198
    {   auto bound = bind(&UpMpd::seqcontrol, this, _1, _2, 0);
188
        addActionMapping("Next", bound);
199
        addActionMapping("Next", bound);
189
    }
200
    }
190
    {   auto bound = bind(&UpMpd::seqcontrol, this, _1, _2, 1);
201
    {   auto bound = bind(&UpMpd::seqcontrol, this, _1, _2, 1);
191
        addActionMapping("Previous", bound);
202
        addActionMapping("Previous", bound);
203
  }
204
205
  addServiceType(serviceIdCM,
206
                 "urn:schemas-upnp-org:service:ConnectionManager:1");
207
  {   auto bound = bind(&UpMpd::getCurrentConnectionIDs, this, _1, _2);
208
      addActionMapping("GetCurrentConnectionIDs", bound);
209
  }
210
  {   auto bound = bind(&UpMpd::getCurrentConnectionInfo, this, _1, _2);
211
      addActionMapping("GetCurrentConnectionInfo", bound);
212
  }
213
  {   auto bound = bind(&UpMpd::getProtocolInfo, this, _1, _2);
214
      addActionMapping("GetProtocolInfo", bound);
192
    }
215
    }
193
}
216
}
194
217
195
// This is called by the polling loop at regular intervals, or when
218
// This is called by the polling loop at regular intervals, or when
196
// triggered, to retrieve changed state variables for each of the
219
// triggered, to retrieve changed state variables for each of the
...
...
207
{
230
{
208
    if (!serviceid.compare(serviceIdRender)) {
231
    if (!serviceid.compare(serviceIdRender)) {
209
        return getEventDataRendering(all, names, values);
232
        return getEventDataRendering(all, names, values);
210
    } else if (!serviceid.compare(serviceIdTransport)) {
233
    } else if (!serviceid.compare(serviceIdTransport)) {
211
        return getEventDataTransport(all, names, values);
234
        return getEventDataTransport(all, names, values);
235
  } else if (!serviceid.compare(serviceIdCM)) {
236
      return getEventDataCM(all, names, values);
212
    } else {
237
    } else {
213
        LOGERR("UpMpd::getEventData: servid? [" << serviceid << "]" << endl);
238
        LOGERR("UpMpd::getEventData: servid? [" << serviceid << "]" << endl);
214
        return UPNP_E_INVALID_PARAM;
239
        return UPNP_E_INVALID_PARAM;
215
    }
240
    }
216
}
241
}
...
...
546
    status["CurrentMediaDuration"] = is_song?
571
    status["CurrentMediaDuration"] = is_song?
547
        upnpduration(mpds.songlenms):"00:00:00";
572
        upnpduration(mpds.songlenms):"00:00:00";
548
    status["CurrentTrackDuration"] = is_song?
573
    status["CurrentTrackDuration"] = is_song?
549
        upnpduration(mpds.songlenms):"00:00:00";
574
        upnpduration(mpds.songlenms):"00:00:00";
550
    status["AVTransportURI"] = uri;
575
    status["AVTransportURI"] = uri;
576
  //status["AVTransportURIMetaData"] = is_song ? m_curMetadata : "";
551
    status["AVTransportURIMetaData"] = is_song?didlmake(mpds) : "";
577
    status["AVTransportURIMetaData"] = is_song ? didlmake(mpds) : "";
552
    status["RelativeTimePosition"] = is_song?
578
    status["RelativeTimePosition"] = is_song?
553
        upnpduration(mpds.songelapsedms):"0:00:00";
579
        upnpduration(mpds.songelapsedms):"0:00:00";
554
    status["AbsoluteTimePosition"] = is_song?
580
    status["AbsoluteTimePosition"] = is_song?
555
        upnpduration(mpds.songelapsedms) : "0:00:00";
581
        upnpduration(mpds.songelapsedms) : "0:00:00";
556
582
557
    status["NextAVTransportURI"] = mapget(mpds.nextsong, "uri");
583
    status["NextAVTransportURI"] = mapget(mpds.nextsong, "uri");
584
  //status["NextAVTransportURIMetaData"] = is_song ? m_nextMetadata : "";
558
    status["NextAVTransportURIMetaData"] = is_song?didlmake(mpds, true) : "";
585
    status["NextAVTransportURIMetaData"] = is_song ? didlmake(mpds, true) : "";
559
586
560
    status["PlaybackStorageMedium"] = playmedium;
587
    status["PlaybackStorageMedium"] = playmedium;
561
    status["PossiblePlaybackStorageMedium"] = "HDD,NETWORK";
588
    status["PossiblePlaybackStorageMedium"] = "HDD,NETWORK";
562
    status["RecordStorageMedium"] = "NOT_IMPLEMENTED";
589
    status["RecordStorageMedium"] = "NOT_IMPLEMENTED";
563
    status["RelativeCounterPosition"] = "0";
590
    status["RelativeCounterPosition"] = "0";
...
...
632
    string metadata;
659
    string metadata;
633
    it = setnext? sc.args.find("NextURIMetaData") : 
660
    it = setnext? sc.args.find("NextURIMetaData") : 
634
        sc.args.find("CurrentURIMetaData");
661
        sc.args.find("CurrentURIMetaData");
635
    if (it != sc.args.end())
662
    if (it != sc.args.end())
636
        metadata = it->second;
663
        metadata = it->second;
664
  //cerr << "SetTransport: setnext " << setnext << " metadata[" << metadata <<
665
  // "]" << endl;
637
666
638
    if ((m_options & upmpdOwnQueue) && !setnext) {
667
    if ((m_options & upmpdOwnQueue) && !setnext) {
639
        // If we own the queue, just clear it before setting the
668
        // If we own the queue, just clear it before setting the
640
        // track.  Else it's difficult to impossible to prevent it
669
        // track.  Else it's difficult to impossible to prevent it
641
        // from growing if upmpdcli restarts. If the option is not set, the
670
        // from growing if upmpdcli restarts. If the option is not set, the
...
...
663
    }
692
    }
664
    int songid;
693
    int songid;
665
    if ((songid = m_mpdcli->insert(uri, setnext?curpos+1:curpos)) < 0) {
694
    if ((songid = m_mpdcli->insert(uri, setnext?curpos+1:curpos)) < 0) {
666
        return UPNP_E_INTERNAL_ERROR;
695
        return UPNP_E_INTERNAL_ERROR;
667
    }
696
    }
697
  if (setnext)
698
      m_nextMetadata = metadata;
699
  else
700
      m_curMetadata = metadata;
701
668
    if (!setnext) {
702
    if (!setnext) {
669
        MpdStatus::State st = mpds.state;
703
        MpdStatus::State st = mpds.state;
670
        // Have to tell mpd which track to play, else it will keep on
704
        // Have to tell mpd which track to play, else it will keep on
671
        // the previous despite of the insertion. The UPnP docs say
705
        // the previous despite of the insertion. The UPnP docs say
672
        // that setAVTransportURI should not change the transport
706
        // that setAVTransportURI should not change the transport
...
...
970
1004
971
    loopWakeup();
1005
    loopWakeup();
972
    return m_mpdcli->seek(abs_seconds) ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
1006
    return m_mpdcli->seek(abs_seconds) ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
973
}
1007
}
974
1008
1009
///////////////// ConnectionManager methods
1010
1011
// "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3,"
1012
// "http-get:*:audio/L16:DLNA.ORG_PN=LPCM,"
1013
// "http-get:*:audio/x-flac:DLNA.ORG_PN=FLAC"
1014
static const string 
1015
myProtocolInfo(
1016
  "http-get:*:audio/wav:*,"
1017
  "http-get:*:audio/wave:*,"
1018
  "http-get:*:audio/x-wav:*,"
1019
  "http-get:*:audio/mpeg:*,"
1020
  "http-get:*:audio/x-mpeg:*,"
1021
  "http-get:*:audio/mp1:*,"
1022
  "http-get:*:audio/aac:*,"
1023
  "http-get:*:audio/flac:*,"
1024
  "http-get:*:audio/x-flac:*,"
1025
  "http-get:*:audio/m4a:*,"
1026
  "http-get:*:audio/mp4:*,"
1027
  "http-get:*:audio/x-m4a:*,"
1028
  "http-get:*:audio/vorbis:*,"
1029
  "http-get:*:audio/ogg:*,"
1030
  "http-get:*:audio/x-ogg:*,"
1031
  "http-get:*:audio/x-scpls:*,"
1032
  "http-get:*:audio/L16;rate=11025;channels=1:*,"
1033
  "http-get:*:audio/L16;rate=22050;channels=1:*,"
1034
  "http-get:*:audio/L16;rate=44100;channels=1:*,"
1035
  "http-get:*:audio/L16;rate=48000;channels=1:*,"
1036
  "http-get:*:audio/L16;rate=88200;channels=1:*,"
1037
  "http-get:*:audio/L16;rate=96000;channels=1:*,"
1038
  "http-get:*:audio/L16;rate=176400;channels=1:*,"
1039
  "http-get:*:audio/L16;rate=192000;channels=1:*,"
1040
  "http-get:*:audio/L16;rate=11025;channels=2:*,"
1041
  "http-get:*:audio/L16;rate=22050;channels=2:*,"
1042
  "http-get:*:audio/L16;rate=44100;channels=2:*,"
1043
  "http-get:*:audio/L16;rate=48000;channels=2:*,"
1044
  "http-get:*:audio/L16;rate=88200;channels=2:*,"
1045
  "http-get:*:audio/L16;rate=96000;channels=2:*,"
1046
  "http-get:*:audio/L16;rate=176400;channels=2:*,"
1047
  "http-get:*:audio/L16;rate=192000;channels=2:*"
1048
  );
1049
1050
bool UpMpd::getEventDataCM(bool all, std::vector<std::string>& names, 
1051
                         std::vector<std::string>& values)
1052
{
1053
  //LOGDEB("UpMpd:getEventDataCM" << endl);
1054
1055
  // Our data never changes, so if this is not an unconditional request,
1056
  // we return nothing.
1057
  if (all) {
1058
      names.push_back("SinkProtocolInfo");
1059
      values.push_back(myProtocolInfo);
1060
  }
1061
  return true;
1062
}
1063
1064
int UpMpd::getCurrentConnectionIDs(const SoapArgs& sc, SoapData& data)
1065
{
1066
  LOGDEB("UpMpd:getCurrentConnectionIDs" << endl);
1067
  data.addarg("ConnectionIDs", "0");
1068
  return UPNP_E_SUCCESS;
1069
}
1070
1071
int UpMpd::getCurrentConnectionInfo(const SoapArgs& sc, SoapData& data)
1072
{
1073
  LOGDEB("UpMpd:getCurrentConnectionInfo" << endl);
1074
  map<string, string>::const_iterator it;
1075
  it = sc.args.find("ConnectionID");
1076
  if (it == sc.args.end() || it->second.empty()) {
1077
      return UPNP_E_INVALID_PARAM;
1078
  }
1079
  if (it->second.compare("0")) {
1080
      return UPNP_E_INVALID_PARAM;
1081
  }
1082
1083
  data.addarg("RcsID", "0");
1084
  data.addarg("AVTransportID", "0");
1085
  data.addarg("ProtocolInfo", "");
1086
  data.addarg("PeerConnectionManager", "");
1087
  data.addarg("PeerConnectionID", "-1");
1088
  data.addarg("Direction", "Input");
1089
  data.addarg("Status", "Unknown");
1090
1091
  return UPNP_E_SUCCESS;
1092
}
1093
1094
int UpMpd::getProtocolInfo(const SoapArgs& sc, SoapData& data)
1095
{
1096
  LOGDEB("UpMpd:getProtocolInfo" << endl);
1097
  data.addarg("Source", "");
1098
  data.addarg("Sink", myProtocolInfo);
1099
1100
  return UPNP_E_SUCCESS;
1101
}
975
1102
976
/////////////////////////////////////////////////////////////////////
1103
/////////////////////////////////////////////////////////////////////
977
// Main program
1104
// Main program
978
1105
979
#include "conftree.hxx"
1106
#include "conftree.hxx"
...
...
1011
1138
1012
static string myDeviceUUID;
1139
static string myDeviceUUID;
1013
1140
1014
static string datadir(DATADIR "/");
1141
static string datadir(DATADIR "/");
1015
static string configdir(CONFIGDIR "/");
1142
static string configdir(CONFIGDIR "/");
1143
1144
// Our XML description data. !Keep description.xml first!
1145
static const char *xmlfilenames[] = {/* keep first */ "description.xml", 
1146
   "RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml"};
1147
1148
static const int xmlfilenamescnt = sizeof(xmlfilenames) / sizeof(char *);
1016
1149
1017
int main(int argc, char *argv[])
1150
int main(int argc, char *argv[])
1018
{
1151
{
1019
    string mpdhost("localhost");
1152
    string mpdhost("localhost");
1020
    int mpdport = 6600;
1153
    int mpdport = 6600;
...
...
1123
    }
1256
    }
1124
    
1257
    
1125
    // Create unique ID
1258
    // Create unique ID
1126
    string UUID = LibUPnP::makeDevUUID(friendlyname);
1259
    string UUID = LibUPnP::makeDevUUID(friendlyname);
1127
1260
1128
  // Read our XML data.
1261
  // Read our XML data to make it available from the virtual directory
1129
    string reason;
1262
    string reason;
1130
1131
  string description;
1132
  string filename = datadir + "description.xml";
1133
  if (!file_to_string(filename, description, &reason)) {
1134
      LOGFAT("Failed reading " << filename << " : " << reason << endl);
1135
      return 1;
1136
  }
1137
  // Update device description with UUID and friendlyname
1138
    description = regsub1("@UUID@", description, UUID);
1139
    description = regsub1("@FRIENDLYNAME@", description, friendlyname);
1140
1141
  string rdc_scdp;
1142
  filename = datadir + "RenderingControl.xml";
1143
  if (!file_to_string(filename, rdc_scdp, &reason)) {
1144
      LOGFAT("Failed reading " << filename << " : " << reason << endl);
1145
      return 1;
1146
  }
1147
1148
  string avt_scdp;
1149
  filename = datadir + "AVTransport.xml";
1150
  if (!file_to_string(filename, avt_scdp, &reason)) {
1151
      LOGFAT("Failed reading " << filename << " : " << reason << endl);
1152
      return 1;
1153
  }
1154
1155
  // List the XML files to be served through http (all will live in '/')
1156
    unordered_map<string, string> xmlfiles;
1263
    unordered_map<string, string> xmlfiles;
1157
  xmlfiles["description.xml"] = description;
1264
  for (int i = 0; i < xmlfilenamescnt; i++) {
1158
  xmlfiles["RenderingControl.xml"] = rdc_scdp;
1265
      string filename = path_cat(datadir, xmlfilenames[i]);
1159
  xmlfiles["AVTransport.xml"] = avt_scdp;
1266
      string data;
1267
      if (!file_to_string(filename, data, &reason)) {
1268
          LOGFAT("Failed reading " << filename << " : " << reason << endl);
1269
          return 1;
1270
      }
1271
      if (i == 0) {
1272
          // Special for description: set UUID and friendlyname
1273
          data = regsub1("@UUID@", data, UUID);
1274
          data = regsub1("@FRIENDLYNAME@", data, friendlyname);
1275
      }
1276
      xmlfiles[xmlfilenames[i]] = data;
1277
  }
1160
1278
1161
    // Initialize the UPnP device object.
1279
    // Initialize the UPnP device object.
1162
    UpMpd device(string("uuid:") + UUID, xmlfiles, &mpdcli,
1280
    UpMpd device(string("uuid:") + UUID, xmlfiles, &mpdcli,
1163
                 ownqueue?UpMpd::upmpdOwnQueue:UpMpd::upmpdNone);
1281
                 ownqueue ? UpMpd::upmpdOwnQueue : UpMpd::upmpdNone);
1164
1282
1283
  // And forever generate state change events.
1165
    LOGDEB("Entering event loop" << endl);
1284
    LOGDEB("Entering event loop" << endl);
1166
1167
  // And forever generate state change events.
1168
    device.eventloop();
1285
    device.eventloop();
1169
1286
1170
    return 0;
1287
    return 0;
1171
}
1288
}
1172
1289