Switch to unified view

a b/src/ohreceiver.cxx
1
/* Copyright (C) 2014 J.F.Dockes
2
 *     This program is free software; you can redistribute it and/or modify
3
 *     it under the terms of the GNU General Public License as published by
4
 *     the Free Software Foundation; either version 2 of the License, or
5
 *     (at your option) any later version.
6
 *
7
 *     This program is distributed in the hope that it will be useful,
8
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *     GNU General Public License for more details.
11
 *
12
 *     You should have received a copy of the GNU General Public License
13
 *     along with this program; if not, write to the
14
 *     Free Software Foundation, Inc.,
15
 *     59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
18
#include "ohreceiver.hxx"
19
20
#include <stdlib.h>                     // for atoi
21
22
#include <upnp/upnp.h>                  // for UPNP_E_SUCCESS, etc
23
24
#include <functional>                   // for _Bind, bind, _1, _2
25
#include <iostream>                     // for endl, etc
26
#include <string>                       // for string, allocator, etc
27
#include <utility>                      // for pair
28
#include <vector>                       // for vector
29
30
#define LOCAL_LOGINC 4
31
#include "libupnpp/log.hxx"             // for LOGDEB, LOGERR
32
#include "libupnpp/soaphelp.hxx"        // for SoapArgs, SoapData, i2s, etc
33
34
#include "mpdcli.hxx"                   // for MpdStatus, UpSong, MPDCli, etc
35
#include "upmpd.hxx"                    // for UpMpd, etc
36
#include "upmpdutils.hxx"               // for didlmake, diffmaps, etc
37
#include "ohplaylist.hxx"
38
39
using namespace std;
40
using namespace std::placeholders;
41
42
static const string sTpProduct("urn:av-openhome-org:service:Receiver:1");
43
static const string sIdProduct("urn:av-openhome-org:serviceId:Receiver");
44
45
OHReceiver::OHReceiver(UpMpd *dev, OHPlaylist *pl, int port)
46
    : UpnpService(sTpProduct, sIdProduct, dev), m_dev(dev), m_pl(pl), 
47
      m_httpport(port)
48
{
49
    dev->addActionMapping(this, "Play", 
50
                          bind(&OHReceiver::play, this, _1, _2));
51
    dev->addActionMapping(this, "Stop", 
52
                          bind(&OHReceiver::stop, this, _1, _2));
53
    dev->addActionMapping(this, "SetSender",
54
                          bind(&OHReceiver::setSender, this, _1, _2));
55
    dev->addActionMapping(this, "Sender", 
56
                          bind(&OHReceiver::sender, this, _1, _2));
57
    dev->addActionMapping(this, "ProtocolInfo",
58
                          bind(&OHReceiver::protocolInfo, this, _1, _2));
59
    dev->addActionMapping(this, "TransportState",
60
                          bind(&OHReceiver::transportState, this, _1, _2));
61
62
    m_httpuri = "http://localhost:"+ SoapHelp::i2s(m_httpport) + 
63
        "/Songcast.wav";
64
}
65
66
// Allowed states: Stopped, Playing,Waiting, Buffering
67
static string mpdsToTransportState(MpdStatus::State st)
68
{
69
    string tstate;
70
71
    switch(st) {
72
    case MpdStatus::MPDS_PLAY: 
73
        tstate = "Playing";
74
        break;
75
    default:
76
        tstate = "Stopped";
77
        break;
78
    }
79
    return tstate;
80
}
81
82
//                                 
83
static const string o_protocolinfo("ohz:*:*:*,ohm:*:*:*,ohu:*.*.*");
84
//static const string o_protocolinfo("ohu:*:*:*");
85
86
bool OHReceiver::makestate(unordered_map<string, string> &st)
87
{
88
    st.clear();
89
90
    const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
91
92
    st["Uri"] = m_uri;
93
    st["Metadata"] = m_metadata;
94
    st["TransportState"] = mpdsToTransportState(mpds.state);
95
    st["ProtocolInfo"] = o_protocolinfo;
96
    return true;
97
}
98
99
bool OHReceiver::getEventData(bool all, std::vector<std::string>& names, 
100
                              std::vector<std::string>& values)
101
{
102
    //LOGDEB("OHReceiver::getEventData" << endl);
103
104
    unordered_map<string, string> state;
105
106
    makestate(state);
107
108
    unordered_map<string, string> changed;
109
    if (all) {
110
        changed = state;
111
    } else {
112
        changed = diffmaps(m_state, state);
113
    }
114
    m_state = state;
115
116
    for (auto it = changed.begin(); it != changed.end(); it++) {
117
        LOGDEB("OHReceiver::getEventData: changed: " << it->first <<
118
               " = " << it->second << endl);
119
        names.push_back(it->first);
120
        values.push_back(it->second);
121
    }
122
123
    return true;
124
}
125
126
void OHReceiver::maybeWakeUp(bool ok)
127
{
128
    if (ok && m_dev)
129
        m_dev->loopWakeup();
130
}
131
132
int OHReceiver::play(const SoapArgs& sc, SoapData& data)
133
{
134
    LOGDEB("OHReceiver::play" << endl);
135
    bool ok = false;
136
137
    if (!m_pl) {
138
        LOGERR("OHReceiver::play: no playlist service" << endl);
139
        return UPNP_E_INTERNAL_ERROR;
140
    }
141
    if (m_uri.empty()) {
142
        LOGERR("OHReceiver::play: no uri" << endl);
143
        return UPNP_E_INTERNAL_ERROR;
144
    }
145
    if (m_metadata.empty()) {
146
        LOGERR("OHReceiver::play: no metadata" << endl);
147
        return UPNP_E_INTERNAL_ERROR;
148
    }
149
        
150
    // We start the songcast command to receive the audio flux and
151
    // export it as HTTP, then insert http URI at the front of the
152
    // queue and execute next/play
153
    if (m_cmd)
154
        m_cmd->zapChild();
155
    m_cmd = shared_ptr<ExecCmd>(new ExecCmd());
156
    vector<string> args;
157
    args.push_back("-u");
158
    args.push_back(m_uri);
159
    LOGDEB("OHReceiver::play: executing scmpdcli" << endl);
160
    ok = m_cmd->startExec("scmpdcli", args, false, false) >= 0;
161
    if (!ok) {
162
        LOGERR("OHReceiver::play: executing scmpdcli failed" <<endl);
163
        goto out;
164
    } else {
165
        LOGDEB("OHReceiver::play: scmpdcli pid "<< m_cmd->getChildPid()<< endl);
166
    }
167
168
    // And insert the appropriate uri in the mpd playlist
169
    ok = m_pl->insertUri(0, m_httpuri, SoapHelp::xmlUnquote(m_metadata));
170
    if (!ok) {
171
        LOGERR("OHReceiver::play: insertUri() failed\n");
172
        goto out;
173
    }
174
    ok = m_dev->m_mpdcli->play(0);
175
    if (!ok) {
176
        LOGERR("OHReceiver::play: play() failed\n");
177
        goto out;
178
    }
179
180
    maybeWakeUp(ok);
181
182
out:
183
    return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
184
}
185
186
int OHReceiver::stop(const SoapArgs& sc, SoapData& data)
187
{
188
    LOGDEB("OHReceiver::stop" << endl);
189
    bool ok = false;
190
    m_cmd->zapChild();
191
    m_cmd = shared_ptr<ExecCmd>(0);
192
    ok = m_dev->m_mpdcli->stop();
193
    maybeWakeUp(ok);
194
    return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
195
}
196
197
int OHReceiver::setSender(const SoapArgs& sc, SoapData& data)
198
{
199
    LOGDEB("OHReceiver::setSender" << endl);
200
    string uri, metadata;
201
    bool ok = sc.getString("Uri", &uri) &&
202
        sc.getString("Metadata", &metadata);
203
204
    if (ok) {
205
        m_uri = uri;
206
        m_metadata = metadata;
207
        LOGDEB("OHReceiver::setSender: uri [" << m_uri << "] meta [" << 
208
               m_metadata << "]" << endl);
209
    }
210
211
    maybeWakeUp(ok);
212
    return ok ? UPNP_E_SUCCESS : UPNP_E_INTERNAL_ERROR;
213
}
214
215
int OHReceiver::sender(const SoapArgs& sc, SoapData& data)
216
{
217
    LOGDEB("OHReceiver::sender" << endl);
218
    data.addarg("Uri", m_uri);
219
    data.addarg("Metadata", m_metadata);
220
    return UPNP_E_SUCCESS;
221
}
222
223
int OHReceiver::transportState(const SoapArgs& sc, SoapData& data)
224
{
225
    //LOGDEB("OHReceiver::transportState" << endl);
226
    const MpdStatus &mpds = m_dev->getMpdStatusNoUpdate();
227
    string tstate = mpdsToTransportState(mpds.state);
228
    data.addarg("Value", tstate);
229
    LOGDEB("OHReceiver::transportState: " << tstate << endl);
230
    return UPNP_E_SUCCESS;
231
}
232
233
int OHReceiver::protocolInfo(const SoapArgs& sc, SoapData& data)
234
{
235
    LOGDEB("OHReceiver::protocolInfo" << endl);
236
    data.addarg("Value", o_protocolinfo);
237
    return UPNP_E_SUCCESS;
238
}