--- a
+++ b/src/ohreceiver.cxx
@@ -0,0 +1,238 @@
+/* Copyright (C) 2014 J.F.Dockes
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "ohreceiver.hxx"
+
+#include <stdlib.h> // for atoi
+
+#include <upnp/upnp.h> // for UPNP_E_SUCCESS, etc
+
+#include <functional> // for _Bind, bind, _1, _2
+#include <iostream> // for endl, etc
+#include <string> // for string, allocator, etc
+#include <utility> // for pair
+#include <vector> // for vector
+
+#define LOCAL_LOGINC 4
+#include "libupnpp/log.hxx" // for LOGDEB, LOGERR
+#include "libupnpp/soaphelp.hxx" // for SoapArgs, SoapData, i2s, etc
+
+#include "mpdcli.hxx" // for MpdStatus, UpSong, MPDCli, etc
+#include "upmpd.hxx" // for UpMpd, etc
+#include "upmpdutils.hxx" // for didlmake, diffmaps, etc
+#include "ohplaylist.hxx"
+
+using namespace std;
+using namespace std::placeholders;
+
+static const string sTpProduct("urn:av-openhome-org:service:Receiver:1");
+static const string sIdProduct("urn:av-openhome-org:serviceId:Receiver");
+
+OHReceiver::OHReceiver(UpMpd *dev, OHPlaylist *pl, int port)
+ : UpnpService(sTpProduct, sIdProduct, dev), m_dev(dev), m_pl(pl),
+ m_httpport(port)
+{
+ dev->addActionMapping(this, "Play",
+ bind(&OHReceiver::play, this, _1, _2));
+ dev->addActionMapping(this, "Stop",
+ bind(&OHReceiver::stop, this, _1, _2));
+ dev->addActionMapping(this, "SetSender",
+ bind(&OHReceiver::setSender, this, _1, _2));
+ dev->addActionMapping(this, "Sender",
+ bind(&OHReceiver::sender, this, _1, _2));
+ dev->addActionMapping(this, "ProtocolInfo",
+ bind(&OHReceiver::protocolInfo, this, _1, _2));
+ dev->addActionMapping(this, "TransportState",
+ bind(&OHReceiver::transportState, this, _1, _2));
+
+ m_httpuri = "http://localhost:"+ SoapHelp::i2s(m_httpport) +
+ "/Songcast.wav";
+}
+
+// Allowed states: Stopped, Playing,Waiting, Buffering
+static string mpdsToTransportState(MpdStatus::State st)
+{
+ string tstate;
+
+ switch(st) {
+ case MpdStatus::MPDS_PLAY:
+ tstate = "Playing";
+ break;
+ default:
+ tstate = "Stopped";
+ break;
+ }
+ return tstate;
+}
+
+//
+static const string o_protocolinfo("ohz:*:*:*,ohm:*:*:*,ohu:*.*.*");
+//static const string o_protocolinfo("ohu:*:*:*");
+
+bool OHReceiver::makestate(unordered_map<string, string> &st)
+{
+ st.clear();
+
+ const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
+
+ st["Uri"] = m_uri;
+ st["Metadata"] = m_metadata;
+ st["TransportState"] = mpdsToTransportState(mpds.state);
+ st["ProtocolInfo"] = o_protocolinfo;
+ return true;
+}
+
+bool OHReceiver::getEventData(bool all, std::vector<std::string>& names,
+ std::vector<std::string>& values)
+{
+ //LOGDEB("OHReceiver::getEventData" << endl);
+
+ unordered_map<string, string> state;
+
+ makestate(state);
+
+ unordered_map<string, string> changed;
+ if (all) {
+ changed = state;
+ } else {
+ changed = diffmaps(m_state, state);
+ }
+ m_state = state;
+
+ for (auto it = changed.begin(); it != changed.end(); it++) {
+ LOGDEB("OHReceiver::getEventData: changed: " << it->first <<
+ " = " << it->second << endl);
+ names.push_back(it->first);
+ values.push_back(it->second);
+ }
+
+ return true;
+}
+
+void OHReceiver::maybeWakeUp(bool ok)
+{
+ if (ok && m_dev)
+ m_dev->loopWakeup();
+}
+
+int OHReceiver::play(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHReceiver::play" << endl);
+ bool ok = false;
+
+ if (!m_pl) {
+ LOGERR("OHReceiver::play: no playlist service" << endl);
+ return UPNP_E_INTERNAL_ERROR;
+ }
+ if (m_uri.empty()) {
+ LOGERR("OHReceiver::play: no uri" << endl);
+ return UPNP_E_INTERNAL_ERROR;
+ }
+ if (m_metadata.empty()) {
+ LOGERR("OHReceiver::play: no metadata" << endl);
+ return UPNP_E_INTERNAL_ERROR;
+ }
+
+ // We start the songcast command to receive the audio flux and
+ // export it as HTTP, then insert http URI at the front of the
+ // queue and execute next/play
+ if (m_cmd)
+ m_cmd->zapChild();
+ m_cmd = shared_ptr<ExecCmd>(new ExecCmd());
+ vector<string> args;
+ args.push_back("-u");
+ args.push_back(m_uri);
+ LOGDEB("OHReceiver::play: executing scmpdcli" << endl);
+ ok = m_cmd->startExec("scmpdcli", args, false, false) >= 0;
+ if (!ok) {
+ LOGERR("OHReceiver::play: executing scmpdcli failed" <<endl);
+ goto out;
+ } else {
+ LOGDEB("OHReceiver::play: scmpdcli pid "<< m_cmd->getChildPid()<< endl);
+ }
+
+ // And insert the appropriate uri in the mpd playlist
+ ok = m_pl->insertUri(0, m_httpuri, SoapHelp::xmlUnquote(m_metadata));
+ if (!ok) {
+ LOGERR("OHReceiver::play: insertUri() failed\n");
+ goto out;
+ }
+ ok = m_dev->m_mpdcli->play(0);
+ if (!ok) {
+ LOGERR("OHReceiver::play: play() failed\n");
+ goto out;
+ }
+
+ maybeWakeUp(ok);
+
+out:
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHReceiver::stop(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHReceiver::stop" << endl);
+ bool ok = false;
+ m_cmd->zapChild();
+ m_cmd = shared_ptr<ExecCmd>(0);
+ ok = m_dev->m_mpdcli->stop();
+ maybeWakeUp(ok);
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHReceiver::setSender(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHReceiver::setSender" << endl);
+ string uri, metadata;
+ bool ok = sc.getString("Uri", &uri) &&
+ sc.getString("Metadata", &metadata);
+
+ if (ok) {
+ m_uri = uri;
+ m_metadata = metadata;
+ LOGDEB("OHReceiver::setSender: uri [" << m_uri << "] meta [" <<
+ m_metadata << "]" << endl);
+ }
+
+ maybeWakeUp(ok);
+ return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
+}
+
+int OHReceiver::sender(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHReceiver::sender" << endl);
+ data.addarg("Uri", m_uri);
+ data.addarg("Metadata", m_metadata);
+ return UPNP_E_SUCCESS;
+}
+
+int OHReceiver::transportState(const SoapArgs& sc, SoapData& data)
+{
+ //LOGDEB("OHReceiver::transportState" << endl);
+ const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
+ string tstate = mpdsToTransportState(mpds.state);
+ data.addarg("Value", tstate);
+ LOGDEB("OHReceiver::transportState: " << tstate << endl);
+ return UPNP_E_SUCCESS;
+}
+
+int OHReceiver::protocolInfo(const SoapArgs& sc, SoapData& data)
+{
+ LOGDEB("OHReceiver::protocolInfo" << endl);
+ data.addarg("Value", o_protocolinfo);
+ return UPNP_E_SUCCESS;
+}