a b/src/ohradio.cpp
1
/* Copyright (C) 2013 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
/////////////// libupnpp OhRadio trial driver
19
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <unistd.h>
23
#include <getopt.h>
24
#include <string.h>
25
26
#include <string>
27
#include <iostream>
28
#include <vector>
29
#include <algorithm>
30
31
#include "libupnpp/upnpplib.hxx"
32
#include "libupnpp/log.hxx"
33
#include "libupnpp/upnpputils.hxx"
34
#include "libupnpp/control/cdirectory.hxx"
35
#include "libupnpp/control/discovery.hxx"
36
#include "libupnpp/control/mediarenderer.hxx"
37
#include "libupnpp/control/ohradio.hxx"
38
#include "libupnpp/control/ohinfo.hxx"
39
40
using namespace std;
41
using namespace UPnPClient;
42
using namespace UPnPP;
43
44
UPnPDeviceDirectory *superdir;
45
46
int channelid;
47
48
class MReporter : public UPnPClient::VarEventReporter {
49
public:
50
    void changed(const char *nm, int value) {
51
        if (!strcmp(nm, "TransportState")) {
52
            string tpstate;
53
            switch(value) {
54
            case OHPlaylist::TPS_Unknown: tpstate = "Unknown"; break;
55
            case OHPlaylist::TPS_Buffering: tpstate = "Buffering"; break;
56
            case OHPlaylist::TPS_Paused: tpstate = "Paused"; break;
57
            case OHPlaylist::TPS_Playing: tpstate = "Playing"; break;
58
            case OHPlaylist::TPS_Stopped: tpstate = "Stopped"; break;
59
            }
60
            cout << "Changed: " << nm << " : " << tpstate << endl;
61
        } else if (!strcmp(nm, "Id")) {
62
            cout << "Changed: " << nm << " : " << value << endl;
63
            channelid = value;
64
        } else {
65
            cout << "Changed: " << nm << " : " << value << endl;
66
        }
67
    }
68
    void changed(const char *nm, const char *value)  {
69
        cout << "Changed: " << nm << " : " << value << endl;
70
    }
71
72
    void changed(const char *nm, UPnPDirObject meta) {
73
        cout << "Changed: " << nm << " : " << meta.dump() << endl;
74
    }
75
76
    void changed(const char * nm, std::vector<int> ids) {
77
        cout << "Changed: " << nm << " : ";
78
        for (unsigned int i = 0; i < ids.size(); i++) {
79
            cout << SoapHelp::i2s(ids[i]) << " ";
80
        }
81
        cout << endl;
82
    }
83
};
84
85
MRDH getRenderer(const string& friendlyName)
86
{
87
    if (superdir == 0) {
88
        superdir = UPnPDeviceDirectory::getTheDir();
89
    }
90
91
    UPnPDeviceDesc ddesc;
92
    if (superdir->getDevByFName(friendlyName, ddesc)) {
93
        return MRDH(new MediaRenderer(ddesc));
94
    }
95
    cerr << "getDevByFname failed" << endl;
96
    return MRDH();
97
}
98
99
void rdMonitor(OHRDH hdl, OHIFH hdlif)
100
{
101
    MReporter reporter;
102
    hdl->installReporter(&reporter);
103
    hdlif->installReporter(&reporter);
104
    while (true) {
105
        static int prevchan;
106
        sleep(2);
107
        string uri;
108
        UPnPDirObject dirent;
109
        if (0&& prevchan != channelid) {
110
            cerr << "New ChannelId: " << channelid << endl;
111
            prevchan = channelid;
112
#if 0
113
            if (hdl->channel(&uri, &dirent) == 0) {
114
                cout << "Channel: uri " << uri << "\nMetadata " <<
115
                    dirent.dump() << endl;
116
            }
117
#endif
118
            int ret;
119
            if ((ret = hdlif->metatext(&dirent)) == 0) {
120
                cout << "Metatext: " << dirent.dump() << endl;
121
            } else {
122
                cerr << "Metatext: failed: " << ret << endl;
123
            }
124
            
125
        }
126
    }
127
}
128
129
void rdIdArray(OHRDH hdl)
130
{
131
    vector<int> ids;
132
    int token = 0;
133
    int ret;
134
    if ((ret = hdl->idArray(&ids, &token)) != 0) {
135
        cerr << "idArray failed: " << ret << endl;
136
        return;
137
    }
138
139
    cout << "token: " << token << ". " << ids.size() << " ids: ";
140
    for (unsigned int i = 0; i < ids.size(); i++) {
141
        cout << SoapHelp::i2s(ids[i]) << " ";
142
    }
143
    cout << endl;
144
}
145
146
string rdReadList(OHRDH hdl, int id = -1)
147
{
148
    vector<int> ids;
149
    int token = 0;
150
    int ret;
151
    if ((ret = hdl->idArray(&ids, &token)) != 0) {
152
        cerr << "idArray failed: " << ret << endl;
153
        return string();
154
    }
155
    vector<OHPlaylist::TrackListEntry> ents;
156
    if ((ret = hdl->readList(ids, &ents)) != 0) {
157
        cerr << "readList failed: " << ret << endl;
158
        return string();
159
    }
160
161
    for (unsigned int i = 0; i < ents.size(); i++) {
162
        if (id == -1) {
163
            cout << "Id: " << SoapHelp::i2s(ents[i].id) <<
164
                " url " << ents[i].url << 
165
                "\nmetadata: " << ents[i].dirent.dump() << "\n";
166
        } else {
167
            if (ents[i].id == id) {
168
                return ents[i].url;
169
            }
170
        }
171
    }
172
    cout << endl;
173
    return string();
174
}
175
176
// Could not get this to work. Gets UPNP_E_BAD_RESPONSE on sendAction??
177
void rdRead(OHRDH hdl, int id)
178
{
179
    UPnPDirObject dirent;
180
    int ret;
181
    if ((ret = hdl->read(id, &dirent)) != 0) {
182
        cerr << "read failed: " << ret << endl;
183
        return;
184
    }
185
    cout << "read: metadata: " << dirent.dump() << endl;
186
}
187
188
void rdSetId(OHRDH hdl, int id)
189
{
190
    int ret;
191
    string url = rdReadList(hdl, id);
192
    if (url.empty()) {
193
        cerr << "Id " << id << " not found\n";
194
        return;
195
    }
196
    if ((ret = hdl->setId(id, url)) != 0) {
197
        cerr << "setId failed: " << ret << endl;
198
        return;
199
    }
200
    cout << "setId ok\n";
201
}
202
203
static char *thisprog;
204
static char usage [] =
205
" -a <renderer>: run idArray\n"
206
" -M <renderer>: monitor OHRadio\n"
207
" -p <renderer>: pause radio\n"
208
" -P <renderer>: play radio\n"
209
" -r <renderer> id: run read\n"
210
" -R <renderer>: run ReadList\n"
211
" -s <renderer> id: run setId\n"
212
" -S <renderer>: stop\n"
213
" \n"
214
;
215
216
static void
217
Usage(void)
218
{
219
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
220
    exit(1);
221
}
222
static int       op_flags;
223
#define OPT_M    0x1
224
#define OPT_a    0x2
225
#define OPT_r    0x4
226
#define OPT_R    0x8
227
#define OPT_s    0x10
228
#define OPT_P    0x20
229
#define OPT_p    0x40
230
#define OPT_S    0x80
231
232
static struct option long_options[] = {
233
    {0, 0, 0, 0}
234
};
235
236
int main(int argc, char *argv[])
237
{
238
    string fname;
239
    string arg;
240
241
    thisprog = argv[0];
242
243
    int ret;
244
    int option_index = 0;
245
    while ((ret = getopt_long(argc, argv, "aMPpRrSs", 
246
                              long_options, &option_index)) != -1) {
247
        switch (ret) {
248
        case 'a': if (op_flags) Usage(); op_flags |= OPT_a; break;
249
        case 'M': if (op_flags) Usage(); op_flags |= OPT_M; break;
250
        case 'P': if (op_flags) Usage(); op_flags |= OPT_P; break;
251
        case 'p': if (op_flags) Usage(); op_flags |= OPT_p; break;
252
        case 'R': if (op_flags) Usage(); op_flags |= OPT_R; break;
253
        case 'r': if (op_flags) Usage(); op_flags |= OPT_r; break;
254
        case 's': if (op_flags) Usage(); op_flags |= OPT_s; break;
255
        case 'S': if (op_flags) Usage(); op_flags |= OPT_S; break;
256
        default:
257
            Usage();
258
        }
259
    }
260
261
    if (op_flags & (OPT_M|OPT_a|OPT_R|OPT_p|OPT_P|OPT_S)) {
262
            if (optind != argc - 1) 
263
                Usage();
264
            fname = argv[optind++];
265
    }
266
    if (op_flags & (OPT_r|OPT_s)) {
267
        if (optind != argc - 2) 
268
                Usage();
269
            fname = argv[optind++];
270
            arg = argv[optind++];
271
    }
272
            
273
    if (Logger::getTheLog("/tmp/ohradio.log") == 0) {
274
        cerr << "Can't initialize log" << endl;
275
        return 1;
276
    }
277
    Logger::getTheLog("")->setLogLevel(Logger::LLDEB1);
278
279
    LibUPnP *mylib = LibUPnP::getLibUPnP();
280
    if (!mylib) {
281
        cerr << "Can't get LibUPnP" << endl;
282
        return 1;
283
    }
284
285
    if (!mylib->ok()) {
286
        cerr << "Lib init failed: " <<
287
            mylib->errAsString("main", mylib->getInitError()) << endl;
288
        return 1;
289
    }
290
    mylib->setLogFileName("/tmp/libupnp.log", LibUPnP::LogLevelDebug);
291
292
    MRDH rdr = getRenderer(fname);
293
    if (!rdr) {
294
        cerr << "Can't connect torenderer\n";
295
        return 1;
296
    }
297
    OHRDH hdl = rdr->ohrd();
298
    if (!hdl) {
299
        cerr << "Device has no OHRadio service" << endl;
300
        return 1;
301
    }
302
    OHIFH hdlif = rdr->ohif();
303
    if (!hdlif) {
304
        cerr << "Device has no OHInfo service" << endl;
305
        return 1;
306
    }
307
    
308
    if ((op_flags & OPT_M)) {
309
        rdMonitor(hdl, hdlif);
310
    } else if ((op_flags & OPT_a)) {
311
        rdIdArray(hdl);
312
    } else if ((op_flags & OPT_R)) {
313
        rdReadList(hdl);
314
    } else if ((op_flags & OPT_p)) {
315
        int ret = hdl->pause();
316
        if (ret) {
317
            cerr << "Pause: " << SoapHelp::i2s(ret);
318
            return 1;
319
        } else {
320
            cout << "Pause Ok\n";
321
        }
322
    } else if ((op_flags & OPT_P)) {
323
        int ret = hdl->play();
324
        if (ret) {
325
            cerr << "Play: " << SoapHelp::i2s(ret);
326
            return 1;
327
        } else {
328
            cout << "Play Ok\n";
329
        }
330
    } else if ((op_flags & OPT_S)) {
331
        int ret = hdl->stop();
332
        if (ret) {
333
            cerr << "Stop: " << SoapHelp::i2s(ret);
334
            return 1;
335
        } else {
336
            cout << "Stop Ok\n";
337
        }
338
    } else if ((op_flags & OPT_r)) {
339
        rdRead(hdl, atoi(arg.c_str()));
340
    } else if ((op_flags & OPT_s)) {
341
        rdSetId(hdl, atoi(arg.c_str()));
342
    } else {
343
        Usage();
344
    }
345
346
    return 0;
347
}