2013-02-23 01:03:27 +01:00
|
|
|
/*
|
|
|
|
* protocolcurl.c: IPTV plugin for the Video Disk Recorder
|
|
|
|
*
|
|
|
|
* See the README file for copyright information and how to reach the author.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "config.h"
|
2015-03-08 13:33:18 +01:00
|
|
|
#include "log.h"
|
2013-02-23 01:03:27 +01:00
|
|
|
#include "protocolcurl.h"
|
|
|
|
|
2013-03-27 21:13:15 +01:00
|
|
|
#ifdef CURLOPT_RTSPHEADER
|
|
|
|
#define USE_RTSP
|
|
|
|
#endif
|
|
|
|
|
2013-02-23 01:03:27 +01:00
|
|
|
#define iptv_curl_easy_setopt(X, Y, Z) \
|
2013-02-25 20:08:08 +01:00
|
|
|
if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { \
|
2014-01-02 20:35:38 +01:00
|
|
|
error("curl_easy_setopt(%s, %s) failed: %s (%d)", #Y, #Z, curl_easy_strerror(res), res); \
|
2013-02-25 20:08:08 +01:00
|
|
|
}
|
2013-02-23 01:03:27 +01:00
|
|
|
|
|
|
|
#define iptv_curl_easy_perform(X) \
|
2013-02-25 20:08:08 +01:00
|
|
|
if ((res = curl_easy_perform((X))) != CURLE_OK) { \
|
2014-01-02 20:35:38 +01:00
|
|
|
error("curl_easy_perform() failed: %s (%d)", curl_easy_strerror(res), res); \
|
2013-02-25 20:08:08 +01:00
|
|
|
}
|
2013-02-23 01:03:27 +01:00
|
|
|
|
|
|
|
cIptvProtocolCurl::cIptvProtocolCurl()
|
|
|
|
: streamUrlM(""),
|
|
|
|
streamParamM(0),
|
2014-02-19 20:38:11 +01:00
|
|
|
streamPortM(0),
|
2013-02-23 01:03:27 +01:00
|
|
|
mutexM(),
|
|
|
|
handleM(NULL),
|
|
|
|
multiM(NULL),
|
|
|
|
headerListM(NULL),
|
2014-04-05 18:01:36 +02:00
|
|
|
ringBufferM(new cRingBufferLinear(IPTV_BUFFER_SIZE, 7 * TS_SIZE, false, "IPTV CURL")),
|
2014-02-19 20:38:11 +01:00
|
|
|
rtspControlM(""),
|
2013-02-23 01:03:27 +01:00
|
|
|
modeM(eModeUnknown),
|
2014-02-19 20:38:11 +01:00
|
|
|
timeoutM(),
|
2013-02-23 01:03:27 +01:00
|
|
|
connectedM(false),
|
|
|
|
pausedM(false)
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
2013-03-11 16:59:59 +01:00
|
|
|
if (ringBufferM) {
|
2013-02-23 01:03:27 +01:00
|
|
|
ringBufferM->SetTimeouts(100, 0);
|
2013-03-11 16:59:59 +01:00
|
|
|
ringBufferM->SetIoThrottle();
|
|
|
|
}
|
2013-02-23 01:03:27 +01:00
|
|
|
Connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
cIptvProtocolCurl::~cIptvProtocolCurl()
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
Disconnect();
|
|
|
|
// Free allocated memory
|
|
|
|
DELETE_POINTER(ringBufferM);
|
|
|
|
}
|
|
|
|
|
2015-03-08 14:47:12 +01:00
|
|
|
int cIptvProtocolCurl::DebugCallback(CURL *handleP, curl_infotype typeP, char *dataP, size_t sizeP, void *userPtrP)
|
|
|
|
{
|
|
|
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
|
|
|
|
|
|
|
if (obj) {
|
|
|
|
switch (typeP) {
|
|
|
|
case CURLINFO_TEXT:
|
|
|
|
debug8("%s INFO %.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
|
|
|
|
break;
|
|
|
|
case CURLINFO_HEADER_IN:
|
|
|
|
debug8("%s HEAD <<< %.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
|
|
|
|
break;
|
|
|
|
case CURLINFO_HEADER_OUT:
|
|
|
|
debug8("%s HEAD >>>\n%.*s", __PRETTY_FUNCTION__, (int)sizeP, dataP);
|
|
|
|
break;
|
|
|
|
case CURLINFO_DATA_IN:
|
|
|
|
debug8("%s DATA <<< %zu", __PRETTY_FUNCTION__, sizeP);
|
|
|
|
break;
|
|
|
|
case CURLINFO_DATA_OUT:
|
|
|
|
debug8("%s DATA >>> %zu", __PRETTY_FUNCTION__, sizeP);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-23 01:03:27 +01:00
|
|
|
size_t cIptvProtocolCurl::WriteCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
|
|
|
{
|
|
|
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
|
|
|
size_t len = sizeP * nmembP;
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %zu, %zu, ) len=%zu", __PRETTY_FUNCTION__, sizeP, nmembP, len);
|
2013-02-23 01:03:27 +01:00
|
|
|
|
|
|
|
if (obj && !obj->PutData((unsigned char *)ptrP, (int)len))
|
|
|
|
return CURL_WRITEFUNC_PAUSE;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t cIptvProtocolCurl::WriteRtspCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
|
|
|
{
|
|
|
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
|
|
|
size_t len = sizeP * nmembP;
|
|
|
|
unsigned char *p = (unsigned char *)ptrP;
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %zu, %zu, ) len=%zu", __PRETTY_FUNCTION__, sizeP, nmembP, len);
|
2013-03-02 00:26:04 +01:00
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Validate packet header ('$') and channel (0)
|
2014-01-07 12:17:30 +01:00
|
|
|
if (obj && (p[0] == 0x24) && (p[1] == 0)) {
|
2013-02-23 01:03:27 +01:00
|
|
|
int length = (p[2] << 8) | p[3];
|
|
|
|
if (length > 3) {
|
2013-02-25 20:08:08 +01:00
|
|
|
// Skip interleave header
|
2013-02-23 01:03:27 +01:00
|
|
|
p += 4;
|
|
|
|
// http://tools.ietf.org/html/rfc3550
|
|
|
|
// http://tools.ietf.org/html/rfc2250
|
2013-02-25 20:08:08 +01:00
|
|
|
// Version
|
2013-02-23 01:03:27 +01:00
|
|
|
unsigned int v = (p[0] >> 6) & 0x03;
|
2013-02-25 20:08:08 +01:00
|
|
|
// Extension bit
|
2013-02-23 01:03:27 +01:00
|
|
|
unsigned int x = (p[0] >> 4) & 0x01;
|
2013-02-25 20:08:08 +01:00
|
|
|
// CSCR count
|
2013-02-23 01:03:27 +01:00
|
|
|
unsigned int cc = p[0] & 0x0F;
|
2013-02-25 20:08:08 +01:00
|
|
|
// Payload type: MPEG2 TS = 33
|
2013-02-23 01:03:27 +01:00
|
|
|
//unsigned int pt = p[1] & 0x7F;
|
2013-02-25 20:08:08 +01:00
|
|
|
// Header lenght
|
2013-02-23 01:03:27 +01:00
|
|
|
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
2013-02-25 20:08:08 +01:00
|
|
|
// Check if extension
|
2013-02-23 01:03:27 +01:00
|
|
|
if (x) {
|
2013-02-25 20:08:08 +01:00
|
|
|
// Extension header length
|
2013-02-23 01:03:27 +01:00
|
|
|
unsigned int ehl = (((p[headerlen + 2] & 0xFF) << 8) |(p[headerlen + 3] & 0xFF));
|
2013-02-25 20:08:08 +01:00
|
|
|
// Update header length
|
2013-02-23 01:03:27 +01:00
|
|
|
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
|
|
|
|
}
|
|
|
|
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
|
|
|
if ((v == 2) && (((length - headerlen) % TS_SIZE) == 0) && (p[headerlen] == TS_SYNC_BYTE)) {
|
|
|
|
// Set argument point to payload in read buffer
|
|
|
|
obj->PutData(&p[headerlen], (length - headerlen));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t cIptvProtocolCurl::DescribeCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
|
|
|
{
|
|
|
|
cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
|
|
|
size_t len = sizeP * nmembP;
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %zu, %zu, ) len=%zu", __PRETTY_FUNCTION__, sizeP, nmembP, len);
|
2013-02-23 01:03:27 +01:00
|
|
|
|
2014-02-19 20:38:11 +01:00
|
|
|
bool found = false;
|
2013-02-23 01:03:27 +01:00
|
|
|
cString control = "";
|
|
|
|
char *p = (char *)ptrP;
|
|
|
|
char *r = strtok(p, "\r\n");
|
|
|
|
|
|
|
|
while (r) {
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %zu, %zu, ) len=%zu r=%s", __PRETTY_FUNCTION__, sizeP, nmembP, len, r);
|
2014-02-19 20:38:11 +01:00
|
|
|
// Look for a media name: "video"
|
|
|
|
if (strstr(r, "m=video")) {
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
// ... and find out its' attribute
|
|
|
|
if (found && strstr(r, "a=control")) {
|
2013-02-23 01:03:27 +01:00
|
|
|
char *s = NULL;
|
2014-02-19 20:38:11 +01:00
|
|
|
if (sscanf(r, "a=control:%255ms", &s) == 1)
|
2013-02-23 01:03:27 +01:00
|
|
|
control = compactspace(s);
|
|
|
|
free(s);
|
2014-02-19 20:38:11 +01:00
|
|
|
break;
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
r = strtok(NULL, "\r\n");
|
2013-03-02 00:26:04 +01:00
|
|
|
}
|
2013-02-23 01:03:27 +01:00
|
|
|
|
|
|
|
if (!isempty(*control) && obj)
|
|
|
|
obj->SetRtspControl(*control);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t cIptvProtocolCurl::HeaderCallback(void *ptrP, size_t sizeP, size_t nmembP, void *dataP)
|
|
|
|
{
|
|
|
|
//cIptvProtocolCurl *obj = reinterpret_cast<cIptvProtocolCurl *>(dataP);
|
|
|
|
size_t len = sizeP * nmembP;
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %zu, %zu, ) len=%zu", __PRETTY_FUNCTION__, sizeP, nmembP, len);
|
2013-02-23 01:03:27 +01:00
|
|
|
|
|
|
|
char *p = (char *)ptrP;
|
|
|
|
char *r = strtok(p, "\r\n");
|
|
|
|
|
|
|
|
while (r) {
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %zu, %zu, ) len=%zu r=%s", __PRETTY_FUNCTION__, sizeP, nmembP, len, r);
|
2013-02-23 01:03:27 +01:00
|
|
|
r = strtok(NULL, "\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cIptvProtocolCurl::SetRtspControl(const char *controlP)
|
|
|
|
{
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (%s)", __PRETTY_FUNCTION__, controlP);
|
2014-02-19 20:38:11 +01:00
|
|
|
cString protocol = ChangeCase(controlP, false).Truncate(7);
|
|
|
|
if (startswith(*protocol, "rtsp://")) {
|
|
|
|
streamUrlM = controlP;
|
|
|
|
rtspControlM = "";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rtspControlM = controlP;
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvProtocolCurl::PutData(unsigned char *dataP, int lenP)
|
|
|
|
{
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %d)", __PRETTY_FUNCTION__, lenP);
|
2013-02-23 01:03:27 +01:00
|
|
|
if (pausedM)
|
|
|
|
return false;
|
|
|
|
if (ringBufferM && (lenP >= 0)) {
|
2013-02-25 20:08:08 +01:00
|
|
|
// Should we pause the transfer ?
|
2013-02-23 01:03:27 +01:00
|
|
|
if (ringBufferM->Free() < (2 * CURL_MAX_WRITE_SIZE)) {
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s Pause free=%d available=%d len=%d", __PRETTY_FUNCTION__,
|
2013-02-25 20:08:08 +01:00
|
|
|
ringBufferM->Free(), ringBufferM->Available(), lenP);
|
2013-02-23 01:03:27 +01:00
|
|
|
pausedM = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
int p = ringBufferM->Put(dataP, lenP);
|
|
|
|
if (p != lenP)
|
|
|
|
ringBufferM->ReportOverflow(lenP - p);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cIptvProtocolCurl::DelData(int lenP)
|
|
|
|
{
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
if (ringBufferM && (lenP >= 0))
|
|
|
|
ringBufferM->Del(lenP);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cIptvProtocolCurl::ClearData()
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
if (ringBufferM)
|
|
|
|
ringBufferM->Clear();
|
|
|
|
}
|
|
|
|
|
2013-03-02 00:26:04 +01:00
|
|
|
unsigned char *cIptvProtocolCurl::GetData(int &lenP)
|
2013-02-23 01:03:27 +01:00
|
|
|
{
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
unsigned char *p = NULL;
|
2013-03-02 00:26:04 +01:00
|
|
|
lenP = 0;
|
2013-02-23 01:03:27 +01:00
|
|
|
if (ringBufferM) {
|
|
|
|
int count = 0;
|
|
|
|
p = ringBufferM->Get(count);
|
|
|
|
#if 0
|
|
|
|
if (p && count >= TS_SIZE) {
|
|
|
|
if (*p != TS_SYNC_BYTE) {
|
|
|
|
for (int i = 1; i < count; ++i) {
|
|
|
|
if (p[i] == TS_SYNC_BYTE) {
|
|
|
|
count = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-03-27 21:13:15 +01:00
|
|
|
error("IPTV skipped %d bytes to sync on TS packet", count);
|
2013-02-23 01:03:27 +01:00
|
|
|
ringBufferM->Del(count);
|
2013-03-02 00:26:04 +01:00
|
|
|
lenP = 0;
|
2013-02-23 01:03:27 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
count -= (count % TS_SIZE);
|
2013-03-02 00:26:04 +01:00
|
|
|
lenP = count;
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvProtocolCurl::Connect()
|
|
|
|
{
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
if (connectedM)
|
|
|
|
return true;
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Initialize the curl session
|
2013-02-23 01:03:27 +01:00
|
|
|
if (!handleM)
|
|
|
|
handleM = curl_easy_init();
|
|
|
|
|
2014-02-19 20:38:11 +01:00
|
|
|
if (handleM && !isempty(*streamUrlM)) {
|
2013-02-23 01:03:27 +01:00
|
|
|
CURLcode res = CURLE_OK;
|
|
|
|
cString netrc = cString::sprintf("%s/netrc", IptvConfig.GetConfigDirectory());
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Verbose output
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_VERBOSE, 1L);
|
2015-03-08 14:47:12 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_DEBUGFUNCTION, cIptvProtocolCurl::DebugCallback);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_DEBUGDATA, this);
|
2013-02-23 01:03:27 +01:00
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Set callbacks
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::WriteCallback);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_HEADERFUNCTION, cIptvProtocolCurl::HeaderCallback);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEHEADER, this);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// No progress meter and no signaling
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_NOPROGRESS, 1L);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_NOSIGNAL, 1L);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Support netrc
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC_FILE, *netrc);
|
|
|
|
|
2014-01-02 20:35:38 +01:00
|
|
|
// Set timeouts
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_CONNECTTIMEOUT, (long)eConnectTimeoutS);
|
2015-02-24 19:28:32 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_LOW_SPEED_LIMIT, (long)eLowSpeedLimitBytes);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_LOW_SPEED_TIME, (long)eLowSpeedTimeoutS);
|
2013-02-23 01:03:27 +01:00
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Set user-agent
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Set URL
|
2013-02-25 22:11:41 +01:00
|
|
|
char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL);
|
|
|
|
streamUrlM = p;
|
|
|
|
curl_free(p);
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_URL, *streamUrlM);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Protocol specific initializations
|
2013-02-23 01:03:27 +01:00
|
|
|
switch (modeM) {
|
2013-03-27 21:13:15 +01:00
|
|
|
#ifdef USE_RTSP
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeRtsp:
|
|
|
|
{
|
|
|
|
cString uri, control, transport, range;
|
|
|
|
|
2014-02-19 20:38:11 +01:00
|
|
|
// Create the listening socket for UDP mode
|
|
|
|
if (!streamParamM)
|
|
|
|
OpenSocket(streamPortM);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Request server options
|
2013-02-23 01:03:27 +01:00
|
|
|
uri = cString::sprintf("%s", *streamUrlM);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
2013-02-28 21:07:14 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Request session description - SDP is delivered in message body and not in the header!
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::DescribeCallback);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
|
|
|
uri = cString::sprintf("%s", *streamUrlM);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
2013-02-28 21:07:14 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, NULL);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, NULL);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Setup media stream
|
2014-02-19 20:38:11 +01:00
|
|
|
if (isempty(*rtspControlM))
|
|
|
|
uri = cString::sprintf("%s", *streamUrlM);
|
|
|
|
else
|
|
|
|
uri = cString::sprintf("%s/%s", *streamUrlM, *rtspControlM);
|
|
|
|
if (streamParamM)
|
|
|
|
transport = "RTP/AVP/TCP;unicast;interleaved=0-1";
|
|
|
|
else
|
|
|
|
transport = cString::sprintf("RTP/AVP;unicast;client_port=%d-%d", streamPortM, streamPortM + 1);
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
|
2013-02-28 21:07:14 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Start playing
|
2013-02-23 01:03:27 +01:00
|
|
|
uri = cString::sprintf("%s/", *streamUrlM);
|
|
|
|
range = "0.000-";
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
2014-02-19 20:38:11 +01:00
|
|
|
//iptv_curl_easy_setopt(handleM, CURLOPT_RANGE, *range);
|
2013-02-28 21:07:14 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Start receiving
|
2014-02-19 20:38:11 +01:00
|
|
|
if (streamParamM) {
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEFUNCTION, cIptvProtocolCurl::WriteRtspCallback);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEDATA, this);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_RECEIVE);
|
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't add handle into multi set
|
|
|
|
isActiveM = true;
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-03-27 21:13:15 +01:00
|
|
|
#endif
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeHttp:
|
|
|
|
case eModeHttps:
|
|
|
|
{
|
2013-02-25 20:08:08 +01:00
|
|
|
// Limit download speed (bytes/s)
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Follow location
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Fail if HTTP return code is >= 400
|
2013-02-23 01:03:27 +01:00
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_FAILONERROR, 1L);
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Set additional headers to prevent caching
|
2013-02-23 01:03:27 +01:00
|
|
|
headerListM = curl_slist_append(headerListM, "Cache-Control: no-store, no-cache, must-revalidate");
|
|
|
|
headerListM = curl_slist_append(headerListM, "Cache-Control: post-check=0, pre-check=0");
|
|
|
|
headerListM = curl_slist_append(headerListM, "Pragma: no-cache");
|
|
|
|
headerListM = curl_slist_append(headerListM, "Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_HTTPHEADER, headerListM);
|
2014-02-19 20:38:11 +01:00
|
|
|
|
|
|
|
// Initialize multi set and add handle into it
|
|
|
|
if (!multiM)
|
|
|
|
multiM = curl_multi_init();
|
|
|
|
if (multiM)
|
|
|
|
curl_multi_add_handle(multiM, handleM);
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eModeFile:
|
2014-02-19 20:38:11 +01:00
|
|
|
{
|
2014-01-02 20:35:38 +01:00
|
|
|
// Set timeout
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_TIMEOUT_MS, 10L);
|
2014-02-19 20:38:11 +01:00
|
|
|
|
|
|
|
// Initialize multi set and add handle into it
|
|
|
|
if (!multiM)
|
|
|
|
multiM = curl_multi_init();
|
|
|
|
if (multiM)
|
|
|
|
curl_multi_add_handle(multiM, handleM);
|
|
|
|
}
|
2014-01-02 20:35:38 +01:00
|
|
|
break;
|
|
|
|
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeUnknown:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-02-19 20:38:11 +01:00
|
|
|
timeoutM.Set(eKeepAliveIntervalMs);
|
2013-02-23 01:03:27 +01:00
|
|
|
connectedM = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvProtocolCurl::Disconnect()
|
|
|
|
{
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
2013-02-25 20:08:08 +01:00
|
|
|
if (!connectedM)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Terminate curl session
|
2014-02-19 20:38:11 +01:00
|
|
|
if (handleM) {
|
|
|
|
// Remove handle from multi set
|
|
|
|
if (multiM) {
|
|
|
|
curl_multi_remove_handle(multiM, handleM);
|
|
|
|
curl_multi_cleanup(multiM);
|
|
|
|
multiM = NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Mode specific tricks
|
2013-02-23 01:03:27 +01:00
|
|
|
switch (modeM) {
|
2013-03-27 21:13:15 +01:00
|
|
|
#ifdef USE_RTSP
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeRtsp:
|
|
|
|
{
|
|
|
|
CURLcode res = CURLE_OK;
|
2013-02-25 20:08:08 +01:00
|
|
|
// Teardown rtsp session
|
2013-02-23 01:03:27 +01:00
|
|
|
cString uri = cString::sprintf("%s/", *streamUrlM);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
|
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
rtspControlM = "";
|
2014-02-19 20:38:11 +01:00
|
|
|
isActiveM = false;
|
|
|
|
// Close the listening socket
|
|
|
|
CloseSocket();
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-03-27 21:13:15 +01:00
|
|
|
#endif
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeHttp:
|
|
|
|
case eModeHttps:
|
|
|
|
case eModeFile:
|
|
|
|
case eModeUnknown:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
// Cleanup curl stuff
|
2013-02-23 01:03:27 +01:00
|
|
|
if (headerListM) {
|
|
|
|
curl_slist_free_all(headerListM);
|
|
|
|
headerListM = NULL;
|
|
|
|
}
|
|
|
|
curl_easy_cleanup(handleM);
|
|
|
|
handleM = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ClearData();
|
|
|
|
connectedM = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvProtocolCurl::Open(void)
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
return Connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvProtocolCurl::Close(void)
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
Disconnect();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-23 14:31:11 +01:00
|
|
|
int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
|
2013-02-23 01:03:27 +01:00
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (, %u)", __PRETTY_FUNCTION__, bufferLenP);
|
2013-02-23 01:03:27 +01:00
|
|
|
int len = 0;
|
|
|
|
if (ringBufferM) {
|
2013-02-25 20:08:08 +01:00
|
|
|
// Fill up the buffer
|
2014-02-19 20:38:11 +01:00
|
|
|
if (handleM) {
|
2013-02-23 01:03:27 +01:00
|
|
|
switch (modeM) {
|
2013-03-27 21:13:15 +01:00
|
|
|
#ifdef USE_RTSP
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeRtsp:
|
|
|
|
{
|
2013-03-02 18:58:37 +01:00
|
|
|
cMutexLock MutexLock(&mutexM);
|
2013-02-25 20:08:08 +01:00
|
|
|
CURLcode res = CURLE_OK;
|
2014-02-19 20:38:11 +01:00
|
|
|
|
|
|
|
// Remember the heart beat
|
|
|
|
if (timeoutM.TimedOut()) {
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s KeepAlive", __PRETTY_FUNCTION__);
|
2014-02-19 20:38:11 +01:00
|
|
|
cString uri = cString::sprintf("%s", *streamUrlM);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
|
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
timeoutM.Set(eKeepAliveIntervalMs);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether UDP or TCP mode used
|
|
|
|
if (streamParamM) {
|
|
|
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_RECEIVE);
|
|
|
|
iptv_curl_easy_perform(handleM);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return cIptvUdpSocket::Read(bufferAddrP, bufferLenP);
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-03-27 21:13:15 +01:00
|
|
|
#endif
|
2013-02-23 01:03:27 +01:00
|
|
|
case eModeFile:
|
|
|
|
case eModeHttp:
|
|
|
|
case eModeHttps:
|
2014-02-19 20:38:11 +01:00
|
|
|
if (multiM) {
|
|
|
|
CURLMcode res;
|
|
|
|
int running_handles;
|
|
|
|
|
|
|
|
do {
|
|
|
|
cMutexLock MutexLock(&mutexM);
|
|
|
|
res = curl_multi_perform(multiM, &running_handles);
|
|
|
|
} while (res == CURLM_CALL_MULTI_PERFORM);
|
2013-02-23 01:03:27 +01:00
|
|
|
|
2014-02-19 20:38:11 +01:00
|
|
|
// Use 20% threshold before continuing to filling up the buffer.
|
2013-03-02 18:58:37 +01:00
|
|
|
mutexM.Lock();
|
2014-04-05 18:01:36 +02:00
|
|
|
if (pausedM && (ringBufferM->Available() < (IPTV_BUFFER_SIZE / 5))) {
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s Continue free=%d available=%d", __PRETTY_FUNCTION__,
|
|
|
|
ringBufferM->Free(), ringBufferM->Available());
|
2014-02-19 20:38:11 +01:00
|
|
|
pausedM = false;
|
|
|
|
curl_easy_pause(handleM, CURLPAUSE_CONT);
|
|
|
|
}
|
2013-03-02 18:58:37 +01:00
|
|
|
mutexM.Unlock();
|
2014-02-19 20:38:11 +01:00
|
|
|
|
|
|
|
// Check if end of file
|
|
|
|
if (running_handles == 0) {
|
|
|
|
int msgcount;
|
|
|
|
mutexM.Lock();
|
|
|
|
CURLMsg *msg = curl_multi_info_read(multiM, &msgcount);
|
|
|
|
mutexM.Unlock();
|
|
|
|
if (msg && (msg->msg == CURLMSG_DONE)) {
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s Done %s (%d)", __PRETTY_FUNCTION__,
|
|
|
|
curl_easy_strerror(msg->data.result), msg->data.result);
|
2014-02-19 20:38:11 +01:00
|
|
|
Disconnect();
|
|
|
|
Connect();
|
|
|
|
}
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-02-25 20:08:08 +01:00
|
|
|
case eModeUnknown:
|
2013-02-23 01:03:27 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ... and try to empty it
|
2013-03-02 00:26:04 +01:00
|
|
|
unsigned char *p = GetData(len);
|
|
|
|
if (p && (len > 0)) {
|
|
|
|
len = min(len, (int)bufferLenP);
|
|
|
|
memcpy(bufferAddrP, p, len);
|
|
|
|
DelData(len);
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s Get %d bytes", __PRETTY_FUNCTION__, len);
|
2013-02-23 01:03:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2014-02-09 18:22:02 +01:00
|
|
|
bool cIptvProtocolCurl::SetSource(const char* locationP, const int parameterP, const int indexP)
|
2013-02-23 01:03:27 +01:00
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s (%s, %d, %d)", __PRETTY_FUNCTION__, locationP, parameterP, indexP);
|
2013-02-23 14:31:11 +01:00
|
|
|
if (!isempty(locationP)) {
|
2013-02-23 01:03:27 +01:00
|
|
|
// Disconnect
|
|
|
|
Disconnect();
|
2013-02-25 22:11:41 +01:00
|
|
|
// Update stream URL
|
|
|
|
streamUrlM = locationP;
|
2013-02-28 21:07:14 +01:00
|
|
|
cString protocol = ChangeCase(streamUrlM, false).Truncate(5);
|
|
|
|
if (startswith(*protocol, "rtsp"))
|
2013-02-23 01:03:27 +01:00
|
|
|
modeM = eModeRtsp;
|
2013-02-28 21:07:14 +01:00
|
|
|
else if (startswith(*protocol, "https"))
|
2013-02-23 01:03:27 +01:00
|
|
|
modeM = eModeHttps;
|
2013-02-28 21:07:14 +01:00
|
|
|
else if (startswith(*protocol, "http"))
|
|
|
|
modeM = eModeHttp;
|
|
|
|
else if (startswith(*protocol, "file"))
|
2013-02-23 01:03:27 +01:00
|
|
|
modeM = eModeFile;
|
|
|
|
else
|
|
|
|
modeM = eModeUnknown;
|
2015-03-08 13:33:18 +01:00
|
|
|
debug1("%s (%s, %d, %d) protocol=%s mode=%d", __PRETTY_FUNCTION__, locationP, parameterP, indexP, *protocol, modeM);
|
2014-02-19 20:38:11 +01:00
|
|
|
// Update stream parameter - force UDP mode for RTSP
|
|
|
|
streamParamM = (modeM == eModeRtsp) ? 0 : parameterP;
|
|
|
|
// Update listen port
|
|
|
|
streamPortM = IptvConfig.GetProtocolBasePort() + indexP * 2;
|
2013-02-23 01:03:27 +01:00
|
|
|
// Reconnect
|
|
|
|
Connect();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-02-09 18:22:02 +01:00
|
|
|
bool cIptvProtocolCurl::SetPid(int pidP, int typeP, bool onP)
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s (%d, %d, %d)", __PRETTY_FUNCTION__, pidP, typeP, onP);
|
2014-02-09 18:22:02 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-23 01:03:27 +01:00
|
|
|
cString cIptvProtocolCurl::GetInformation(void)
|
|
|
|
{
|
2015-03-08 13:33:18 +01:00
|
|
|
debug16("%s", __PRETTY_FUNCTION__);
|
2013-02-23 01:03:27 +01:00
|
|
|
return cString::sprintf("%s [%d]", *streamUrlM, streamParamM);
|
|
|
|
}
|