|
a/src/cdplugins/tidal.cxx |
|
b/src/cdplugins/tidal.cxx |
|
... |
|
... |
21 |
|
21 |
|
22 |
#include <string>
|
22 |
#include <string>
|
23 |
#include <vector>
|
23 |
#include <vector>
|
24 |
#include <string.h>
|
24 |
#include <string.h>
|
25 |
#include <librtmp/rtmp.h>
|
25 |
#include <librtmp/rtmp.h>
|
|
|
26 |
#include <upnp/upnp.h>
|
26 |
|
27 |
|
27 |
#include "cmdtalk.h"
|
28 |
#include "cmdtalk.h"
|
28 |
|
29 |
|
29 |
#include "pathut.h"
|
30 |
#include "pathut.h"
|
30 |
#include "smallut.h"
|
31 |
#include "smallut.h"
|
|
... |
|
... |
108 |
return string();
|
109 |
return string();
|
109 |
}
|
110 |
}
|
110 |
if (mimetype.empty()) {
|
111 |
if (mimetype.empty()) {
|
111 |
unordered_map<string, string> res;
|
112 |
unordered_map<string, string> res;
|
112 |
if (!cmd.callproc("mimetype", {{"path", path}}, res)) {
|
113 |
if (!cmd.callproc("mimetype", {{"path", path}}, res)) {
|
113 |
LOGERR("Tidal::get_media_url: slave failure\n");
|
114 |
LOGERR("Tidal::get_mimetype: slave failure\n");
|
114 |
return string();
|
115 |
return string();
|
115 |
}
|
116 |
}
|
116 |
|
117 |
|
117 |
auto it = res.find("mimetype");
|
118 |
auto it = res.find("mimetype");
|
118 |
if (it == res.end()) {
|
119 |
if (it == res.end()) {
|
|
... |
|
... |
132 |
inf->last_modified = 0;
|
133 |
inf->last_modified = 0;
|
133 |
inf->mime = get_mimetype(path);
|
134 |
inf->mime = get_mimetype(path);
|
134 |
return 0;
|
135 |
return 0;
|
135 |
}
|
136 |
}
|
136 |
|
137 |
|
|
|
138 |
class StreamHandle {
|
|
|
139 |
public:
|
|
|
140 |
StreamHandle() : rtmp(nullptr), http_handle(nullptr) {
|
|
|
141 |
}
|
|
|
142 |
~StreamHandle() {
|
|
|
143 |
if (rtmp) {
|
|
|
144 |
RTMP_Close(rtmp);
|
|
|
145 |
RTMP_Free(rtmp);
|
|
|
146 |
}
|
|
|
147 |
if (http_handle) {
|
|
|
148 |
UpnpCloseHttpGet(http_handle);
|
|
|
149 |
}
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
RTMP *rtmp;
|
|
|
153 |
void *http_handle;
|
|
|
154 |
int len;
|
|
|
155 |
};
|
|
|
156 |
|
137 |
void *Tidal::Internal::open(const string& path)
|
157 |
void *Tidal::Internal::open(const string& path)
|
138 |
{
|
158 |
{
|
139 |
LOGDEB("Tidal::open: " << path << endl);
|
159 |
LOGDEB("Tidal::open: " << path << endl);
|
140 |
string media_url = get_media_url(path);
|
160 |
string media_url = get_media_url(path);
|
141 |
if (media_url.empty()) {
|
161 |
if (media_url.empty()) {
|
142 |
return nullptr;
|
162 |
return nullptr;
|
143 |
}
|
163 |
}
|
|
|
164 |
if (media_url.find("http") == 0) {
|
|
|
165 |
void *http_handle;
|
|
|
166 |
char *content_type;
|
|
|
167 |
int content_length;
|
|
|
168 |
int httpstatus;
|
|
|
169 |
int code = UpnpOpenHttpGet(media_url.c_str(), &http_handle,
|
|
|
170 |
&content_type, &content_length,
|
|
|
171 |
&httpstatus, 30);
|
|
|
172 |
LOGDEB("Tidal::open: UpnpOpenHttpGet: ret " << code <<
|
|
|
173 |
" mtype " << content_type << " length " << content_length <<
|
|
|
174 |
" HTTP status " << httpstatus << endl);
|
|
|
175 |
if (code) {
|
|
|
176 |
LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
|
|
|
177 |
" mtype " << content_type << " length " << content_length <<
|
|
|
178 |
" HTTP status " << httpstatus << endl);
|
|
|
179 |
return nullptr;
|
|
|
180 |
}
|
|
|
181 |
StreamHandle *hdl = new StreamHandle;
|
|
|
182 |
hdl->http_handle = http_handle;
|
|
|
183 |
hdl->len = content_length;
|
|
|
184 |
return hdl;
|
|
|
185 |
} else {
|
144 |
RTMP *rtmp = RTMP_Alloc();
|
186 |
RTMP *rtmp = RTMP_Alloc();
|
145 |
RTMP_Init(rtmp);
|
187 |
RTMP_Init(rtmp);
|
146 |
|
188 |
|
147 |
// Writable copy of url
|
189 |
// Writable copy of url
|
148 |
if (!RTMP_SetupURL(rtmp, strdup(media_url.c_str()))) {
|
190 |
if (!RTMP_SetupURL(rtmp, strdup(media_url.c_str()))) {
|
149 |
LOGERR("Tidal::open: RTMP_SetupURL failed for [" <<
|
191 |
LOGERR("Tidal::open: RTMP_SetupURL failed for [" <<
|
150 |
media_url << "]\n");
|
192 |
media_url << "]\n");
|
151 |
RTMP_Free(rtmp);
|
193 |
RTMP_Free(rtmp);
|
152 |
return nullptr;
|
194 |
return nullptr;
|
153 |
}
|
195 |
}
|
154 |
if (!RTMP_Connect(rtmp, NULL)) {
|
196 |
if (!RTMP_Connect(rtmp, NULL)) {
|
155 |
LOGERR("Tidal::open: RTMP_Connect failed for [" <<
|
197 |
LOGERR("Tidal::open: RTMP_Connect failed for [" <<
|
156 |
media_url << "]\n");
|
198 |
media_url << "]\n");
|
157 |
RTMP_Free(rtmp);
|
199 |
RTMP_Free(rtmp);
|
158 |
return nullptr;
|
200 |
return nullptr;
|
159 |
}
|
201 |
}
|
160 |
if (!RTMP_ConnectStream(rtmp, 0)) {
|
202 |
if (!RTMP_ConnectStream(rtmp, 0)) {
|
161 |
LOGERR("Tidal::open: RTMP_ConnectStream failed for [" <<
|
203 |
LOGERR("Tidal::open: RTMP_ConnectStream failed for [" <<
|
162 |
media_url << "]\n");
|
204 |
media_url << "]\n");
|
163 |
RTMP_Free(rtmp);
|
205 |
RTMP_Free(rtmp);
|
164 |
return nullptr;
|
206 |
return nullptr;
|
165 |
}
|
|
|
166 |
|
207 |
}
|
167 |
return rtmp;
|
208 |
StreamHandle *hdl = new StreamHandle;
|
|
|
209 |
hdl->rtmp = rtmp;
|
|
|
210 |
return hdl;
|
|
|
211 |
}
|
168 |
}
|
212 |
}
|
169 |
|
213 |
|
170 |
int Tidal::Internal::read(void *hdl, char* buf, size_t cnt)
|
214 |
int Tidal::Internal::read(void *_hdl, char* buf, size_t cnt)
|
171 |
{
|
215 |
{
|
172 |
LOGDEB("Tidal::read: " << cnt << endl);
|
216 |
LOGDEB("Tidal::read: " << cnt << endl);
|
173 |
if (!hdl)
|
217 |
if (!_hdl)
|
174 |
return -1;
|
218 |
return -1;
|
175 |
RTMP *rtmp = (RTMP *)hdl;
|
219 |
|
|
|
220 |
// The pupnp http code has a default 1MB buffer size which is much
|
|
|
221 |
// too big for us (too slow, esp. because tidal will stall).
|
176 |
if (cnt > 100 * 1024)
|
222 |
if (cnt > 100 * 1024)
|
177 |
cnt = 100 * 1024;
|
223 |
cnt = 100 * 1024;
|
|
|
224 |
|
|
|
225 |
StreamHandle *hdl = (StreamHandle *)_hdl;
|
|
|
226 |
|
|
|
227 |
if (hdl->rtmp) {
|
|
|
228 |
RTMP *rtmp = hdl->rtmp;
|
178 |
size_t totread = 0;
|
229 |
size_t totread = 0;
|
179 |
while (totread < cnt) {
|
230 |
while (totread < cnt) {
|
180 |
int didread = RTMP_Read(rtmp, buf+totread, cnt-totread);
|
231 |
int didread = RTMP_Read(rtmp, buf+totread, cnt-totread);
|
181 |
//LOGDEB("Tidal::read: RTMP_Read returned: " << didread << endl);
|
232 |
//LOGDEB("Tidal::read: RTMP_Read returned: " << didread << endl);
|
182 |
if (didread <= 0)
|
233 |
if (didread <= 0)
|
183 |
break;
|
234 |
break;
|
184 |
totread += didread;
|
235 |
totread += didread;
|
185 |
}
|
236 |
}
|
186 |
LOGDEB("Tidal::read: total read: " << totread << endl);
|
237 |
LOGDEB("Tidal::read: total read: " << totread << endl);
|
187 |
return totread > 0 ? totread : -1;
|
238 |
return totread > 0 ? totread : -1;
|
|
|
239 |
} else if (hdl->http_handle) {
|
|
|
240 |
int code = UpnpReadHttpGet(hdl->http_handle, buf, &cnt, 30);
|
|
|
241 |
if (code) {
|
|
|
242 |
LOGERR("Tidal::read: UpnpReadHttpGet returned " << code << endl);
|
|
|
243 |
return -1;
|
|
|
244 |
}
|
|
|
245 |
return int(cnt);
|
|
|
246 |
} else {
|
|
|
247 |
LOGERR("Tidal::read: neither rtmp nor http\n");
|
|
|
248 |
return -1;
|
|
|
249 |
}
|
188 |
}
|
250 |
}
|
189 |
|
251 |
|
190 |
off_t Tidal::Internal::seek(void *hdl, off_t offs, int whence)
|
252 |
off_t Tidal::Internal::seek(void *hdl, off_t offs, int whence)
|
191 |
{
|
253 |
{
|
192 |
LOGDEB("Tidal::seek\n");
|
254 |
LOGDEB("Tidal::seek\n");
|
193 |
return -1;
|
255 |
return -1;
|
194 |
}
|
256 |
}
|
195 |
|
257 |
|
196 |
void Tidal::Internal::close(void *hdl)
|
258 |
void Tidal::Internal::close(void *_hdl)
|
197 |
{
|
259 |
{
|
198 |
LOGDEB("Tidal::close\n");
|
260 |
LOGDEB("Tidal::close\n");
|
199 |
if (hdl) {
|
261 |
StreamHandle *hdl = (StreamHandle *)_hdl;
|
200 |
RTMP *rtmp = (RTMP *)hdl;
|
262 |
delete hdl;
|
201 |
RTMP_Close(rtmp);
|
|
|
202 |
RTMP_Free(rtmp);
|
|
|
203 |
}
|
|
|
204 |
}
|
263 |
}
|
205 |
|
264 |
|
206 |
|
265 |
|
207 |
VirtualDir::FileOps Tidal::getFileOps()
|
266 |
VirtualDir::FileOps Tidal::getFileOps()
|
208 |
{
|
267 |
{
|