|
a/src/avtransport.cxx |
|
b/src/avtransport.cxx |
|
... |
|
... |
21 |
|
21 |
|
22 |
#include <functional>
|
22 |
#include <functional>
|
23 |
#include <iostream>
|
23 |
#include <iostream>
|
24 |
#include <map>
|
24 |
#include <map>
|
25 |
#include <utility>
|
25 |
#include <utility>
|
|
|
26 |
#include <regex>
|
26 |
|
27 |
|
27 |
#include "libupnpp/log.hxx"
|
28 |
#include "libupnpp/log.hxx"
|
28 |
#include "libupnpp/soaphelp.hxx"
|
29 |
#include "libupnpp/soaphelp.hxx"
|
29 |
#include "libupnpp/upnpavutils.hxx"
|
30 |
#include "libupnpp/upnpavutils.hxx"
|
30 |
|
31 |
|
31 |
#include "mpdcli.hxx"
|
32 |
#include "mpdcli.hxx"
|
32 |
#include "ohplaylist.hxx"
|
33 |
#include "ohplaylist.hxx"
|
33 |
#include "upmpd.hxx"
|
34 |
#include "upmpd.hxx"
|
34 |
#include "upmpdutils.hxx"
|
35 |
#include "upmpdutils.hxx"
|
35 |
#include "smallut.h"
|
36 |
#include "smallut.h"
|
|
|
37 |
#include "pathut.h"
|
|
|
38 |
#include "conftree.h"
|
|
|
39 |
#include "mediaserver/cdplugins/cdplugin.hxx"
|
36 |
|
40 |
|
37 |
// For testing upplay with a dumb renderer.
|
41 |
// For testing upplay with a dumb renderer.
|
38 |
// #define NO_SETNEXT
|
42 |
// #define NO_SETNEXT
|
39 |
|
43 |
|
40 |
using namespace std;
|
44 |
using namespace std;
|
41 |
using namespace std::placeholders;
|
45 |
using namespace std::placeholders;
|
42 |
|
46 |
|
43 |
static const string sIdTransport("urn:upnp-org:serviceId:AVTransport");
|
47 |
static const string sIdTransport("urn:upnp-org:serviceId:AVTransport");
|
44 |
static const string sTpTransport("urn:schemas-upnp-org:service:AVTransport:1");
|
48 |
static const string sTpTransport("urn:schemas-upnp-org:service:AVTransport:1");
|
|
|
49 |
|
|
|
50 |
// this is used for translating urls for the very special use of
|
|
|
51 |
// Kazoo/Lumin + ohcredentials. Actually, this should be configurable
|
|
|
52 |
// as there is no need for the media server, which is used to do the
|
|
|
53 |
// real data access to run on this host.
|
|
|
54 |
static string upnphost;
|
45 |
|
55 |
|
46 |
UpMpdAVTransport::UpMpdAVTransport(UpMpd *dev, bool noev)
|
56 |
UpMpdAVTransport::UpMpdAVTransport(UpMpd *dev, bool noev)
|
47 |
: UpnpService(sTpTransport, sIdTransport, dev, noev), m_dev(dev), m_ohp(0)
|
57 |
: UpnpService(sTpTransport, sIdTransport, dev, noev), m_dev(dev), m_ohp(0)
|
48 |
{
|
58 |
{
|
49 |
m_dev->addActionMapping(this,"SetAVTransportURI",
|
59 |
m_dev->addActionMapping(this,"SetAVTransportURI",
|
|
... |
|
... |
87 |
this, _1, _2, 0));
|
97 |
this, _1, _2, 0));
|
88 |
m_dev->addActionMapping(this, "Previous",
|
98 |
m_dev->addActionMapping(this, "Previous",
|
89 |
bind(&UpMpdAVTransport::seqcontrol,
|
99 |
bind(&UpMpdAVTransport::seqcontrol,
|
90 |
this, _1, _2, 1));
|
100 |
this, _1, _2, 1));
|
91 |
|
101 |
|
|
|
102 |
unsigned short usport;
|
|
|
103 |
m_dev->ipv4(&upnphost, &usport);
|
|
|
104 |
|
92 |
// This would make our life easier, but it's incompatible if
|
105 |
// This would make our life easier, but it's incompatible if
|
93 |
// ohplaylist is also in use, so refrain.
|
106 |
// ohplaylist is also in use, so refrain.
|
94 |
// dev->m_mpdcli->consume(true);
|
107 |
// dev->m_mpdcli->consume(true);
|
95 |
#ifdef NO_SETNEXT
|
108 |
#ifdef NO_SETNEXT
|
96 |
// If no setnext, we'd like to fake stopping at each track but this does not work because mpd goes into PAUSED PLAY at the end of track, not STOP.
|
109 |
// If no setnext, we'd like to fake stopping at each track but this does not work because mpd goes into PAUSED PLAY at the end of track, not STOP.
|
|
... |
|
... |
364 |
m_tpstate = newtpstate;
|
377 |
m_tpstate = newtpstate;
|
365 |
LOGDEB1("UpMpdAVTransport::getEventDataTransport: " << chgdata << endl);
|
378 |
LOGDEB1("UpMpdAVTransport::getEventDataTransport: " << chgdata << endl);
|
366 |
return true;
|
379 |
return true;
|
367 |
}
|
380 |
}
|
368 |
|
381 |
|
|
|
382 |
// Hack for working with OHCredentials. The CP aware of this
|
|
|
383 |
// (Kazoo/Lumin mostly) will send URIs like qobuz:// tidal:// and
|
|
|
384 |
// expect the renderer to know what to do with them. We transform them
|
|
|
385 |
// so that they point to our media server gateway (which should be
|
|
|
386 |
// running of course for this to work).
|
|
|
387 |
static bool maybeMorphSpecialUri(string& uri)
|
|
|
388 |
{
|
|
|
389 |
if (uri.find("http://") == 0 || uri.find("https://") == 0) {
|
|
|
390 |
return true;
|
|
|
391 |
}
|
|
|
392 |
|
|
|
393 |
static string sport;
|
|
|
394 |
if (sport.empty()) {
|
|
|
395 |
std::unique_lock<std::mutex>(g_configlock);
|
|
|
396 |
int port = CDPluginServices::default_microhttpport();
|
|
|
397 |
if (!g_config->get("plgmicrohttpport", sport)) {
|
|
|
398 |
sport = SoapHelp::i2s(port);
|
|
|
399 |
}
|
|
|
400 |
}
|
|
|
401 |
|
|
|
402 |
// http://wiki.openhome.org/wiki/Av:Developer:Eriskay:StreamingServices
|
|
|
403 |
// Tidal and qobuz tracks added by Kazoo / Lumin:
|
|
|
404 |
// tidal://track?version=1&trackId=[tidal_track_id]
|
|
|
405 |
// qobuz://track?version=2&trackId=[qobuz_track_id]
|
|
|
406 |
|
|
|
407 |
string se =
|
|
|
408 |
"(tidal|qobuz)://track\\?version=([[:digit:]]+)&trackId=([[:digit:]]+)";
|
|
|
409 |
std::regex e(se);
|
|
|
410 |
std::smatch mr;
|
|
|
411 |
bool found = std::regex_match(uri, mr, e);
|
|
|
412 |
if (found) {
|
|
|
413 |
string pathprefix = CDPluginServices::getpathprefix(mr[1]);
|
|
|
414 |
|
|
|
415 |
// The microhttpd code actually only cares about getting a
|
|
|
416 |
// trackId parameter. Make it look what the plugins normally
|
|
|
417 |
// generate anyway:
|
|
|
418 |
string path = path_cat(pathprefix,
|
|
|
419 |
"track?version=1&trackId=" + mr[3].str());
|
|
|
420 |
uri = string("http://") + upnphost + ":" + sport + path;
|
|
|
421 |
}
|
|
|
422 |
return found;
|
|
|
423 |
}
|
|
|
424 |
|
369 |
// http://192.168.4.4:8200/MediaItems/246.mp3
|
425 |
// http://192.168.4.4:8200/MediaItems/246.mp3
|
370 |
int UpMpdAVTransport::setAVTransportURI(const SoapIncoming& sc,
|
426 |
int UpMpdAVTransport::setAVTransportURI(const SoapIncoming& sc,
|
371 |
SoapOutgoing& data, bool setnext)
|
427 |
SoapOutgoing& data, bool setnext)
|
372 |
{
|
428 |
{
|
373 |
#ifdef NO_SETNEXT
|
429 |
#ifdef NO_SETNEXT
|
|
... |
|
... |
381 |
string uri;
|
437 |
string uri;
|
382 |
bool found = setnext? sc.get("NextURI", &uri) : sc.get("CurrentURI", &uri);
|
438 |
bool found = setnext? sc.get("NextURI", &uri) : sc.get("CurrentURI", &uri);
|
383 |
if (!found) {
|
439 |
if (!found) {
|
384 |
return UPNP_E_INVALID_PARAM;
|
440 |
return UPNP_E_INVALID_PARAM;
|
385 |
}
|
441 |
}
|
|
|
442 |
if (!maybeMorphSpecialUri(uri)) {
|
|
|
443 |
LOGERR("Set(Next)AVTransportURI: bad uri: " << uri << endl);
|
|
|
444 |
return UPNP_E_INVALID_PARAM;
|
|
|
445 |
}
|
|
|
446 |
|
386 |
string metadata;
|
447 |
string metadata;
|
387 |
found = setnext ? sc.get("NextURIMetaData", &metadata) :
|
448 |
found = setnext ? sc.get("NextURIMetaData", &metadata) :
|
388 |
sc.get("CurrentURIMetaData", &metadata);
|
449 |
sc.get("CurrentURIMetaData", &metadata);
|
389 |
LOGDEB("Set(next)AVTransportURI: next " << setnext << " uri " << uri <<
|
450 |
LOGDEB("Set(next)AVTransportURI: next " << setnext << " uri " << uri <<
|
390 |
" metadata[" << metadata << "]" << endl);
|
451 |
" metadata[" << metadata << "]" << endl);
|