--- a/src/cdplugins/tidal.cxx
+++ b/src/cdplugins/tidal.cxx
@@ -38,10 +38,41 @@
using json = nlohmann::json;
using namespace UPnPProvider;
+class StreamHandle {
+public:
+ StreamHandle() : rtmp(nullptr), http_handle(nullptr), offset(0) {
+ }
+ ~StreamHandle() {
+ clear();
+ }
+ void clear() {
+ if (rtmp) {
+ RTMP_Close(rtmp);
+ RTMP_Free(rtmp);
+ }
+ if (http_handle) {
+ LOGDEB("StreamHandle:~: closing http handle\n");
+ UpnpCloseHttpGet(http_handle);
+ LOGDEB("StreamHandle:~: close done\n");
+ }
+ len = 0;
+ offset = 0;
+ media_url.clear();
+ path.clear();
+ }
+ string path;
+ string media_url;
+ RTMP *rtmp;
+ void *http_handle;
+ int len;
+ off_t offset;
+ time_t opentime;
+};
+
class Tidal::Internal {
public:
Internal(const vector<string>& pth, const string& hp, const string& pp)
- : path(pth), httphp(hp), pathprefix(pp), lasttime(0) { }
+ : path(pth), httphp(hp), pathprefix(pp) { }
bool maybeStartCmd(const string&);
string get_media_url(const std::string& path);
@@ -63,11 +94,9 @@
// choice only. Initialized once
string mimetype;
- // Cached uri translation for the vdir: we do this in getinfo()
- // and reuse in open()
- string lastpath;
- string lastmediaurl;
- time_t lasttime;
+ // Cached uri translation and stream: set in getinfo() and reused
+ // in open()
+ StreamHandle laststream;
};
bool Tidal::Internal::maybeStartCmd(const string& who)
@@ -89,32 +118,6 @@
return true;
}
-string Tidal::Internal::get_media_url(const std::string& path)
-{
- if (!maybeStartCmd("get_media_url")) {
- return string();
- }
- if (lastpath.compare(path) || (time(0) - lasttime > 10)) {
- unordered_map<string, string> res;
- if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
- LOGERR("Tidal::get_media_url: slave failure\n");
- return string();
- }
-
- auto it = res.find("media_url");
- if (it == res.end()) {
- LOGERR("Tidal::get_media_url: no media url in result\n");
- return string();
- }
- lastmediaurl = it->second;
- lastpath = path;
- lasttime = time(0);
- }
-
- LOGDEB("Tidal: got media url [" << lastmediaurl << "]\n");
- return lastmediaurl;
-}
-
string Tidal::Internal::get_mimetype(const std::string& path)
{
if (!maybeStartCmd("get_mimetype")) {
@@ -138,31 +141,39 @@
return mimetype;
}
-class StreamHandle {
-public:
- StreamHandle() : rtmp(nullptr), http_handle(nullptr) {
- }
- ~StreamHandle() {
- if (rtmp) {
- RTMP_Close(rtmp);
- RTMP_Free(rtmp);
- }
- if (http_handle) {
- LOGDEB("StreamHandle:~: closing http handle\n");
- UpnpCloseHttpGet(http_handle);
- LOGDEB("StreamHandle:~: close done\n");
- }
- }
-
- RTMP *rtmp;
- void *http_handle;
- int len;
-};
-
+string Tidal::Internal::get_media_url(const std::string& path)
+{
+ if (!maybeStartCmd("get_media_url")) {
+ return string();
+ }
+ time_t now = time(0);
+ if (laststream.path.compare(path) || (now - laststream.opentime > 10)) {
+ unordered_map<string, string> res;
+ if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
+ LOGERR("Tidal::get_media_url: slave failure\n");
+ return string();
+ }
+
+ auto it = res.find("media_url");
+ if (it == res.end()) {
+ LOGERR("Tidal::get_media_url: no media url in result\n");
+ return string();
+ }
+ laststream.clear();
+ laststream.path = path;
+ laststream.media_url = it->second;
+ laststream.opentime = now;
+ }
+
+ LOGDEB("Tidal: got media url [" << laststream.media_url << "]\n");
+ return laststream.media_url;
+}
int Tidal::Internal::getinfo(const std::string& path, VirtualDir::FileInfo *inf)
{
LOGDEB("Tidal::getinfo: " << path << endl);
+
+ laststream.clear();
string media_url = get_media_url(path);
if (media_url.empty()) {
return -1;
@@ -171,34 +182,26 @@
inf->last_modified = 0;
inf->mime = get_mimetype(path);
if (media_url.find("http") == 0) {
- void *http_handle;
char *content_type;
- int content_length;
int httpstatus;
- int code = UpnpOpenHttpGet(media_url.c_str(), &http_handle,
- &content_type, &content_length,
+ int code = UpnpOpenHttpGet(media_url.c_str(), &laststream.http_handle,
+ &content_type, &laststream.len,
&httpstatus, 30);
LOGDEB("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
- " mtype " << content_type << " length " << content_length <<
+ " mtype " << content_type << " length " << laststream.len <<
" HTTP status " << httpstatus << endl);
if (code) {
LOGERR("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
- " mtype " << content_type << " length " << content_length <<
+ " mtype " << content_type << " length " << laststream.len <<
" HTTP status " << httpstatus << endl);
} else {
- inf->file_length = content_length;
+ inf->file_length = laststream.len;
LOGDEB("Tidal:getinfo: got file length "<< inf->file_length <<endl);
}
- // Doc says to free this, but it causes malloc issues
- //free(content_type);
- StreamHandle hdl;
- hdl.http_handle = http_handle;
- // Let StreamHandle clean up
}
LOGDEB("Tidal::getinfo: returning\n");
return 0;
}
-
void *Tidal::Internal::open(const string& path)
{
@@ -208,27 +211,33 @@
return nullptr;
}
if (media_url.find("http") == 0) {
- void *http_handle;
- char *content_type;
- int content_length;
- int httpstatus;
- int code = UpnpOpenHttpGet(media_url.c_str(), &http_handle,
- &content_type, &content_length,
- &httpstatus, 30);
- LOGDEB("Tidal::open: UpnpOpenHttpGet: ret " << code <<
- " mtype " << content_type << " length " << content_length <<
- " HTTP status " << httpstatus << endl);
- if (code) {
- LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
- " mtype " << content_type << " length " << content_length <<
- " HTTP status " << httpstatus << endl);
- return nullptr;
- }
- // Doc says to free this, but it causes malloc issues
+ if (laststream.http_handle == nullptr) {
+ char *content_type;
+ int httpstatus;
+ int code = UpnpOpenHttpGet(media_url.c_str(),
+ &laststream.http_handle,
+ &content_type, &laststream.len,
+ &httpstatus, 30);
+ LOGDEB("Tidal::open: UpnpOpenHttpGet: ret " << code <<
+ " mtype " << content_type << " length " <<
+ laststream.len <<
+ " HTTP status " << httpstatus << endl);
+ if (code) {
+ LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
+ " mtype " << content_type << " length " <<
+ laststream.len <<
+ " HTTP status " << httpstatus << endl);
+ return nullptr;
+ }
+ }
+ // Doc says to free content_type, but it causes malloc issues
//free(content_type);
StreamHandle *hdl = new StreamHandle;
- hdl->http_handle = http_handle;
- hdl->len = content_length;
+ hdl->http_handle = laststream.http_handle;
+ hdl->len = laststream.len;
+ hdl->media_url = media_url;
+ laststream.http_handle = nullptr;
+ laststream.clear();
return hdl;
} else {
RTMP *rtmp = RTMP_Alloc();
@@ -267,8 +276,9 @@
// The pupnp http code has a default 1MB buffer size which is much
// too big for us (too slow, esp. because tidal will stall).
- if (cnt > 100 * 1024)
- cnt = 100 * 1024;
+ const int mybsize = 200 * 1024;
+ if (cnt > mybsize)
+ cnt = mybsize;
StreamHandle *hdl = (StreamHandle *)_hdl;
@@ -283,6 +293,7 @@
totread += didread;
}
LOGDEB("Tidal::read: total read: " << totread << endl);
+ hdl->offset += totread;
return totread > 0 ? totread : -1;
} else if (hdl->http_handle) {
int code = UpnpReadHttpGet(hdl->http_handle, buf, &cnt, 30);
@@ -290,6 +301,7 @@
LOGERR("Tidal::read: UpnpReadHttpGet returned " << code << endl);
return -1;
}
+ hdl->offset += cnt;
return int(cnt);
} else {
LOGERR("Tidal::read: neither rtmp nor http\n");
@@ -297,10 +309,40 @@
}
}
-off_t Tidal::Internal::seek(void *hdl, off_t offs, int whence)
-{
- LOGDEB("Tidal::seek\n");
- return -1;
+off_t Tidal::Internal::seek(void *_hdl, off_t offs, int whence)
+{
+ StreamHandle *hdl = (StreamHandle *)_hdl;
+ LOGDEB("Tidal::seek: offs "<< offs << " whence " << whence << " current " <<
+ hdl->offset << endl);
+ if (whence == 0) {
+ hdl->offset = offs;
+ } else if (whence == 1) {
+ hdl->offset += offs;
+ } else if (whence == 2) {
+ hdl->offset = hdl->len + offs;
+ }
+ if (hdl->http_handle) {
+ UpnpCloseHttpGet(hdl->http_handle);
+ char *content_type;
+ int content_length;
+ int httpstatus;
+ int code = UpnpOpenHttpGetEx(hdl->media_url.c_str(), &hdl->http_handle,
+ &content_type, &content_length,
+ &httpstatus, hdl->offset, 2000000000, 30);
+ LOGERR("Tidal::seek to " << hdl->offset <<
+ " UpnpOpenHttpGetEx: ret " << code <<
+ " mtype " << content_type << " length " << content_length <<
+ " HTTP status " << httpstatus << endl);
+ if (code) {
+ LOGERR("Tidal::seek to " << hdl->offset <<
+ " UpnpOpenHttpGetEx: ret " << code <<
+ " mtype " << content_type << " length " << content_length <<
+ " HTTP status " << httpstatus << endl);
+ return -1;
+ }
+ }
+
+ return 0;
}
void Tidal::Internal::close(void *_hdl)