1
0
mirror of https://github.com/rofafor/vdr-plugin-iptv.git synced 2023-10-10 11:37:03 +00:00

Compare commits

..

9 Commits

Author SHA1 Message Date
Rolf Ahrenberg
41fe387018 Updated translation files. 2013-03-06 09:54:06 +02:00
Rolf Ahrenberg
2c287ef667 Updated HISTORY. 2013-03-06 09:50:43 +02:00
Rolf Ahrenberg
d7dfa0aad6 Fixed the channel editor and added extra locking into the CURL protocol. 2013-03-02 19:58:37 +02:00
Rolf Ahrenberg
72a9f21006 Changed a pointer to a reference in order to stabilize the CURL protocol and always remember to close the sid scanner. 2013-03-02 01:26:04 +02:00
Rolf Ahrenberg
d84fd79d14 Reverted the CURL locking changes and updated some word wrapping. 2013-03-01 12:22:41 +02:00
Rolf Ahrenberg
f632650547 Added case-insensitive comparisions and fixed data types of a few CURL options. 2013-02-28 22:07:14 +02:00
Rolf Ahrenberg
980aafb206 Modified the filter code. 2013-02-26 00:34:45 +02:00
Rolf Ahrenberg
45192e924a Fixed URL decoding and section filter protection. 2013-02-25 23:11:41 +02:00
Rolf Ahrenberg
e4657c1820 Fixed bugs found in the CURL implementation (Thanks to Jeremy Hall). 2013-02-25 21:08:08 +02:00
20 changed files with 191 additions and 163 deletions

View File

@@ -193,3 +193,9 @@ VDR Plugin 'iptv' Revision History
- Updated for vdr-1.7.38.
- Added a new CURL protocol for HTTP/HTTPS.
2013-03-06: Version 1.2.1
- Fixed bugs found in the CURL implementation (Thanks
to Jeremy Hall).
- Fixed the channel editor.

10
README
View File

@@ -89,11 +89,11 @@ Configuration:
- channels.conf
TV4;IPTV:40:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:4:0:0:0
TV3;IPTV:30:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0
TV2;IPTV:20:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:2:0:0:0
TV1;IPTV:10:S=1|P=0|F=CURL|U=http%3A//foo%3Abar@127.0.0.1%3A3000/TS/2|A=0:I:0:512:650:2321:0:1:0:0:0
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
TV4;IPTV:60:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:6:0:0:0
TV3;IPTV:50:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:5:0:0:0
TV2;IPTV:40:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:4:0:0:0
TV1;IPTV:30:S=1|P=0|F=CURL|U=http%3A//foo%3Abar@127.0.0.1%3A3000/TS/2|A=0:I:0:512:650:2321:0:3:0:0:0
TV1;IPTV:20:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:2:0:0:0
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
^ ^ ^ ^ ^ ^ ^
| | | | | | Source type ("I")

View File

@@ -5,44 +5,45 @@
*
*/
#include <ctype.h>
#include <vdr/tools.h>
#include "common.h"
uint16_t ts_pid(const uint8_t *buf)
uint16_t ts_pid(const uint8_t *bufP)
{
return (uint16_t)(((buf[1] & 0x1f) << 8) + buf[2]);
return (uint16_t)(((bufP[1] & 0x1f) << 8) + bufP[2]);
}
uint8_t payload(const uint8_t *tsp)
uint8_t payload(const uint8_t *bufP)
{
if (!(tsp[3] & 0x10)) // no payload?
if (!(bufP[3] & 0x10)) // no payload?
return 0;
if (tsp[3] & 0x20) { // adaptation field?
if (tsp[4] > 183) // corrupted data?
if (bufP[3] & 0x20) { // adaptation field?
if (bufP[4] > 183) // corrupted data?
return 0;
else
return (uint8_t)((184 - 1) - tsp[4]);
return (uint8_t)((184 - 1) - bufP[4]);
}
return 184;
}
const char *id_pid(const u_short Pid)
const char *id_pid(const u_short pidP)
{
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
if (Pid == section_filter_table[i].pid)
if (pidP == section_filter_table[i].pid)
return section_filter_table[i].tag;
}
return "---";
}
int select_single_desc(int descriptor, const int usecs, const bool selectWrite)
int select_single_desc(int descriptorP, const int usecsP, const bool selectWriteP)
{
// Wait for data
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = usecs;
tv.tv_usec = usecsP;
// Use select
fd_set infd;
fd_set outfd;
@@ -50,17 +51,28 @@ int select_single_desc(int descriptor, const int usecs, const bool selectWrite)
FD_ZERO(&infd);
FD_ZERO(&outfd);
FD_ZERO(&errfd);
FD_SET(descriptor, &errfd);
if (selectWrite)
FD_SET(descriptor, &outfd);
FD_SET(descriptorP, &errfd);
if (selectWriteP)
FD_SET(descriptorP, &outfd);
else
FD_SET(descriptor, &infd);
int retval = select(descriptor + 1, &infd, &outfd, &errfd, &tv);
FD_SET(descriptorP, &infd);
int retval = select(descriptorP + 1, &infd, &outfd, &errfd, &tv);
// Check if error
ERROR_IF_RET(retval < 0, "select()", return retval);
return retval;
}
cString ChangeCase(const cString &strP, bool upperP)
{
cString res(strP);
char *p = (char *)*res;
while (p && *p) {
*p = upperP ? toupper(*p) : tolower(*p);
++p;
}
return res;
}
const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE] =
{
/* description tag pid tid mask */
@@ -72,4 +84,3 @@ const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE]
{trNOOP("EIT (0x6X)"), "EIT", 0x12, 0x60, 0xF0},
{trNOOP("TDT (0x70)"), "TDT", 0x14, 0x70, 0xFF},
};

View File

@@ -66,10 +66,11 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
uint16_t ts_pid(const uint8_t *buf);
uint8_t payload(const uint8_t *tsp);
const char *id_pid(const u_short Pid);
int select_single_desc(int descriptor, const int usecs, const bool selectWrite);
uint16_t ts_pid(const uint8_t *bufP);
uint8_t payload(const uint8_t *bufP);
const char *id_pid(const u_short pidP);
int select_single_desc(int descriptorP, const int usecsP, const bool selectWriteP);
cString ChangeCase(const cString &strP, bool upperP);
struct section_filter_table_type {
const char *description;

View File

@@ -41,9 +41,7 @@ cIptvDevice::cIptvDevice(unsigned int indexP)
// Start section handler for iptv device
StartSectionHandler();
// Sid scanner must be created after the section handler
pSidScannerM = new cSidScanner();
if (pSidScannerM)
AttachFilter(pSidScannerM);
AttachFilter(pSidScannerM = new cSidScanner());
// Check if dvr fifo exists
struct stat sb;
cString filename = cString::sprintf(IPTV_DVR_FILENAME, deviceIndexM);
@@ -58,8 +56,6 @@ cIptvDevice::cIptvDevice(unsigned int indexP)
cIptvDevice::~cIptvDevice()
{
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
// Stop section handler of iptv device
StopSectionHandler();
DELETE_POINTER(pIptvStreamerM);
DELETE_POINTER(pUdpProtocolM);
DELETE_POINTER(pCurlProtocolM);
@@ -68,12 +64,11 @@ cIptvDevice::~cIptvDevice()
DELETE_POINTER(pExtProtocolM);
DELETE_POINTER(tsBufferM);
DELETE_POINTER(pPidScannerM);
// Detach and destroy sid filter
if (pSidScannerM) {
Detach(pSidScannerM);
DELETE_POINTER(pSidScannerM);
}
DELETE_POINTER(pSidScannerM);
// Stop section handler of iptv device
StopSectionHandler();
// Destroy all filters
cMutexLock MutexLock(&mutexM);
for (int i = 0; i < eMaxSecFilterCount; ++i)
DeleteFilter(i);
// Close dvr fifo
@@ -143,6 +138,7 @@ cString cIptvDevice::GetFiltersInformation(void)
unsigned int count = 0;
cString s("Active section filters:\n");
// loop through active section filters
cMutexLock MutexLock(&mutexM);
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (secFiltersM[i]) {
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
@@ -321,7 +317,7 @@ bool cIptvDevice::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const
// loop through section filter table
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
int index = IptvConfig.GetDisabledFilters(i);
// check if matches
// Check if matches
if ((index >= 0) && (index < SECTION_FILTER_TABLE_SIZE) &&
(section_filter_table[index].pid == pidP) && (section_filter_table[index].tid == tidP) &&
(section_filter_table[index].mask == maskP)) {
@@ -337,11 +333,11 @@ int cIptvDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
// Check if disabled by user
if (!IptvConfig.GetSectionFiltering())
return -1;
// Lock
cMutexLock MutexLock(&mutexM);
// Blacklist check, refuse certain filters
if (IsBlackListed(pidP, tidP, maskP))
return -1;
// Lock
cMutexLock MutexLock(&mutexM);
// Search the next free filter slot
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (!secFiltersM[i]) {
@@ -396,7 +392,7 @@ bool cIptvDevice::OpenDvr(void)
void cIptvDevice::CloseDvr(void)
{
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
if (sidScanEnabledM && pSidScannerM )
pSidScannerM->Close();
if (pIptvStreamerM)
pIptvStreamerM->Close();
@@ -418,7 +414,7 @@ bool cIptvDevice::HasInternalCam(void)
void cIptvDevice::ResetBuffering(void)
{
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
// pad prefill to multiple of TS_SIZE
// Pad prefill to multiple of TS_SIZE
tsBufferPrefillM = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize()) *
IptvConfig.GetTsBufferPrefillRatio() / 100;
tsBufferPrefillM -= (tsBufferPrefillM % TS_SIZE);

2
iptv.c
View File

@@ -21,7 +21,7 @@
#define GITVERSION ""
#endif
const char VERSION[] = "1.2.0" GITVERSION;
const char VERSION[] = "1.2.1" GITVERSION;
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
class cPluginIptv : public cPlugin {

View File

@@ -5,7 +5,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.0\n"
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"

View File

@@ -5,7 +5,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.0\n"
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.0\n"
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"

View File

@@ -5,7 +5,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.0\n"
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"

View File

@@ -5,7 +5,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.0\n"
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"

View File

@@ -5,7 +5,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.0\n"
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"

View File

@@ -10,10 +10,14 @@
#include "protocolcurl.h"
#define iptv_curl_easy_setopt(X, Y, Z) \
if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { error("curl_easy_setopt(%s, %s, %s) failed: %d\n", #X, #Y, #Z, res); }
if ((res = curl_easy_setopt((X), (Y), (Z))) != CURLE_OK) { \
error("curl_easy_setopt(%s, %s, %s) failed: %d\n", #X, #Y, #Z, res); \
}
#define iptv_curl_easy_perform(X) \
if ((res = curl_easy_perform((X))) != CURLE_OK) { error("curl_easy_perform(%s) failed: %d\n", #X, res); }
if ((res = curl_easy_perform((X))) != CURLE_OK) { \
error("curl_easy_perform(%s) failed: %d\n", #X, res); \
}
cIptvProtocolCurl::cIptvProtocolCurl()
: streamUrlM(""),
@@ -22,7 +26,8 @@ cIptvProtocolCurl::cIptvProtocolCurl()
handleM(NULL),
multiM(NULL),
headerListM(NULL),
ringBufferM(new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()), 7 * TS_SIZE)),
ringBufferM(new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()),
7 * TS_SIZE, false, "IPTV CURL")),
rtspControlM(),
modeM(eModeUnknown),
connectedM(false),
@@ -60,30 +65,30 @@ size_t cIptvProtocolCurl::WriteRtspCallback(void *ptrP, size_t sizeP, size_t nme
size_t len = sizeP * nmembP;
unsigned char *p = (unsigned char *)ptrP;
//debug("cIptvProtocolCurl::%s(%zu)", __FUNCTION__, len);
// validate packet header ('$') and channel (0)
// Validate packet header ('$') and channel (0)
if (obj && (p[0] == 0x24 ) && (p[1] == 0)) {
int length = (p[2] << 8) | p[3];
if (length > 3) {
// skip interleave header
// Skip interleave header
p += 4;
// http://tools.ietf.org/html/rfc3550
// http://tools.ietf.org/html/rfc2250
// version
// Version
unsigned int v = (p[0] >> 6) & 0x03;
// extension bit
// Extension bit
unsigned int x = (p[0] >> 4) & 0x01;
// cscr count
// CSCR count
unsigned int cc = p[0] & 0x0F;
// payload type: MPEG2 TS = 33
// Payload type: MPEG2 TS = 33
//unsigned int pt = p[1] & 0x7F;
// header lenght
// Header lenght
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
// check if extension
// Check if extension
if (x) {
// extension header length
// Extension header length
unsigned int ehl = (((p[headerlen + 2] & 0xFF) << 8) |(p[headerlen + 3] & 0xFF));
// update header length
// Update header length
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
}
// Check that rtp is version 2 and payload contains multiple of TS packet data
@@ -116,7 +121,7 @@ size_t cIptvProtocolCurl::DescribeCallback(void *ptrP, size_t sizeP, size_t nmem
free(s);
}
r = strtok(NULL, "\r\n");
}
}
if (!isempty(*control) && obj)
obj->SetRtspControl(*control);
@@ -155,9 +160,10 @@ bool cIptvProtocolCurl::PutData(unsigned char *dataP, int lenP)
if (pausedM)
return false;
if (ringBufferM && (lenP >= 0)) {
// should be pause the transfer?
// Should we pause the transfer ?
if (ringBufferM->Free() < (2 * CURL_MAX_WRITE_SIZE)) {
debug("cIptvProtocolCurl::%s(pause): free=%d available=%d len=%d", __FUNCTION__, ringBufferM->Free(), ringBufferM->Available(), lenP);
debug("cIptvProtocolCurl::%s(pause): free=%d available=%d len=%d", __FUNCTION__,
ringBufferM->Free(), ringBufferM->Available(), lenP);
pausedM = true;
return false;
}
@@ -184,12 +190,12 @@ void cIptvProtocolCurl::ClearData()
ringBufferM->Clear();
}
unsigned char *cIptvProtocolCurl::GetData(unsigned int *lenP)
unsigned char *cIptvProtocolCurl::GetData(int &lenP)
{
cMutexLock MutexLock(&mutexM);
//debug("cIptvProtocolCurl::%s()", __FUNCTION__);
unsigned char *p = NULL;
*lenP = 0;
lenP = 0;
if (ringBufferM) {
int count = 0;
p = ringBufferM->Get(count);
@@ -204,13 +210,13 @@ unsigned char *cIptvProtocolCurl::GetData(unsigned int *lenP)
}
error("IPTV skipped %d bytes to sync on TS packet\n", count);
ringBufferM->Del(count);
*lenP = 0;
lenP = 0;
return NULL;
}
}
#endif
count -= (count % TS_SIZE);
*lenP = count;
lenP = count;
}
return p;
@@ -223,7 +229,7 @@ bool cIptvProtocolCurl::Connect()
if (connectedM)
return true;
// initialize the curl session
// Initialize the curl session
if (!handleM)
handleM = curl_easy_init();
if (!multiM)
@@ -234,78 +240,78 @@ bool cIptvProtocolCurl::Connect()
cString netrc = cString::sprintf("%s/netrc", IptvConfig.GetConfigDirectory());
#ifdef DEBUG
// verbose output
// Verbose output
iptv_curl_easy_setopt(handleM, CURLOPT_VERBOSE, 1L);
#endif
// set callbacks
// Set callbacks
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);
// no progress meter and no signaling
// No progress meter and no signaling
iptv_curl_easy_setopt(handleM, CURLOPT_NOPROGRESS, 1L);
iptv_curl_easy_setopt(handleM, CURLOPT_NOSIGNAL, 1L);
// support netrc
// Support netrc
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL);
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC_FILE, *netrc);
// set timeout
// Set timeout
iptv_curl_easy_setopt(handleM, CURLOPT_CONNECTTIMEOUT, (long)eConnectTimeoutS);
// set user-agent
// Set user-agent
iptv_curl_easy_setopt(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
// set url
//char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL);
//streamUrlM = p;
//curl_free(p);
// Set URL
char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL);
streamUrlM = p;
curl_free(p);
iptv_curl_easy_setopt(handleM, CURLOPT_URL, *streamUrlM);
// protocol specific initializations
// Protocol specific initializations
switch (modeM) {
case eModeRtsp:
{
cString uri, control, transport, range;
// request server options
// Request server options
uri = cString::sprintf("%s", *streamUrlM);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
iptv_curl_easy_perform(handleM);
// request session description - SDP is delivered in message body and not in the header!
// Request session description - SDP is delivered in message body and not in the header!
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);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
iptv_curl_easy_perform(handleM);
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, NULL);
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, NULL);
// setup media stream
// Setup media stream
uri = cString::sprintf("%s/%s", *streamUrlM, *rtspControlM);
transport = "RTP/AVP/TCP;unicast;interleaved=0-1";
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
iptv_curl_easy_perform(handleM);
// start playing
// Start playing
uri = cString::sprintf("%s/", *streamUrlM);
range = "0.000-";
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
iptv_curl_easy_setopt(handleM, CURLOPT_RANGE, *range);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
iptv_curl_easy_perform(handleM);
// start receiving
// Start receiving
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, CURL_RTSPREQ_RECEIVE);
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_RECEIVE);
iptv_curl_easy_perform(handleM);
}
break;
@@ -313,16 +319,16 @@ bool cIptvProtocolCurl::Connect()
case eModeHttp:
case eModeHttps:
{
// limit download speed (bytes/s)
// Limit download speed (bytes/s)
iptv_curl_easy_setopt(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L);
// follow location
// Follow location
iptv_curl_easy_setopt(handleM, CURLOPT_FOLLOWLOCATION, 1L);
// fail if HTTP return code is >= 400
// Fail if HTTP return code is >= 400
iptv_curl_easy_setopt(handleM, CURLOPT_FAILONERROR, 1L);
// set additional headers to prevent caching
// Set additional headers to prevent caching
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");
@@ -337,7 +343,7 @@ bool cIptvProtocolCurl::Connect()
break;
}
// add handle into multi set
// Add handle into multi set
curl_multi_add_handle(multiM, handleM);
connectedM = true;
@@ -351,13 +357,17 @@ bool cIptvProtocolCurl::Disconnect()
{
cMutexLock MutexLock(&mutexM);
debug("cIptvProtocolCurl::%s()", __FUNCTION__);
if (handleM) {
// mode specific tricks
if (!connectedM)
return true;
// Terminate curl session
if (handleM && multiM) {
// Mode specific tricks
switch (modeM) {
case eModeRtsp:
{
CURLcode res = CURLE_OK;
// teardown rtsp session
// Teardown rtsp session
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);
@@ -374,16 +384,16 @@ bool cIptvProtocolCurl::Disconnect()
break;
}
// cleanup curl stuff
// Cleanup curl stuff
if (headerListM) {
curl_slist_free_all(headerListM);
headerListM = NULL;
}
curl_multi_remove_handle(multiM, handleM);
curl_multi_cleanup(multiM);
multiM = NULL;
curl_easy_cleanup(handleM);
handleM = NULL;
curl_multi_cleanup(multiM);
multiM = NULL;
}
ClearData();
@@ -409,21 +419,20 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
//debug("cIptvProtocolCurl::%s()", __FUNCTION__);
int len = 0;
if (ringBufferM) {
// fill up the buffer
// Fill up the buffer
if (handleM && multiM) {
switch (modeM) {
case eModeRtsp:
{
//CURLcode res = CURLE_OK;
//iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE);
//iptv_curl_easy_perform(handleM);
// @todo - how to detect eof?
cMutexLock MutexLock(&mutexM);
CURLcode res = CURLE_OK;
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_RECEIVE);
iptv_curl_easy_perform(handleM);
// @todo - How to detect eof?
}
break;
case eModeFile:
break;
case eModeHttp:
case eModeHttps:
{
@@ -431,24 +440,29 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
int running_handles;
do {
cMutexLock MutexLock(&mutexM);
res = curl_multi_perform(multiM, &running_handles);
} while (res == CURLM_CALL_MULTI_PERFORM);
// shall be continue filling up the buffer?
// Shall we continue filling up the buffer?
mutexM.Lock();
if (pausedM && (ringBufferM->Free() > ringBufferM->Available())) {
debug("cIptvProtocolCurl::%s(continue): free=%d available=%d", __FUNCTION__, ringBufferM->Free(), ringBufferM->Available());
debug("cIptvProtocolCurl::%s(continue): free=%d available=%d", __FUNCTION__,
ringBufferM->Free(), ringBufferM->Available());
pausedM = false;
curl_easy_pause(handleM, CURLPAUSE_CONT);
}
mutexM.Unlock();
// check end of file
// 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)) {
debug("cIptvProtocolCurl::%s(done): %s (%d)", __FUNCTION__, curl_easy_strerror(msg->data.result), msg->data.result);
debug("cIptvProtocolCurl::%s(done): %s (%d)", __FUNCTION__,
curl_easy_strerror(msg->data.result), msg->data.result);
Disconnect();
Connect();
}
@@ -456,17 +470,18 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
}
break;
case eModeUnknown:
default:
break;
}
}
// ... and try to empty it
unsigned char *p = GetData(&bufferLenP);
if (p && (bufferLenP > 0)) {
memcpy(bufferAddrP, p, bufferLenP);
DelData(bufferLenP);
len = bufferLenP;
unsigned char *p = GetData(len);
if (p && (len > 0)) {
len = min(len, (int)bufferLenP);
memcpy(bufferAddrP, p, len);
DelData(len);
//debug("cIptvProtocolCurl::%s(): get %d bytes", __FUNCTION__, len);
}
}
@@ -480,22 +495,20 @@ bool cIptvProtocolCurl::Set(const char* locationP, const int parameterP, const i
if (!isempty(locationP)) {
// Disconnect
Disconnect();
// Update stream URL: colons (%3A) and pipes (%7C) shall be decoded
char *s = strdup(locationP);
strreplace(s, "%3A", ":");
strreplace(s, "%7C", "|");
streamUrlM = s;
free(s);
if (startswith(*streamUrlM, "rtsp") || startswith(*streamUrlM, "RTSP"))
// Update stream URL
streamUrlM = locationP;
cString protocol = ChangeCase(streamUrlM, false).Truncate(5);
if (startswith(*protocol, "rtsp"))
modeM = eModeRtsp;
else if (startswith(*streamUrlM, "https") || startswith(*streamUrlM, "HTTPS"))
modeM = eModeHttp;
else if (startswith(*streamUrlM, "http") || startswith(*streamUrlM, "HTTP"))
else if (startswith(*protocol, "https"))
modeM = eModeHttps;
else if (startswith(*streamUrlM, "file") || startswith(*streamUrlM, "FILE"))
else if (startswith(*protocol, "http"))
modeM = eModeHttp;
else if (startswith(*protocol, "file"))
modeM = eModeFile;
else
modeM = eModeUnknown;
debug("cIptvProtocolCurl::%s(): %s (%d)", __FUNCTION__, *protocol, modeM);
// Update stream parameter
streamParamM = parameterP;
// Reconnect

View File

@@ -29,7 +29,6 @@ private:
};
enum {
eConnectTimeoutS = 5, // in seconds
eSelectTimeoutMs = 10, // in milliseconds
eMaxDownloadSpeedMBits = 20 // in megabits per second
};
@@ -55,7 +54,7 @@ private:
bool PutData(unsigned char *dataP, int lenP);
void DelData(int lenP);
void ClearData(void);
unsigned char *GetData(unsigned int *lenP);
unsigned char *GetData(int &lenP);
public:
cIptvProtocolCurl();

View File

@@ -55,8 +55,9 @@ bool cIptvProtocolHttp::Connect(void)
"Connection: Close\r\n"
"\r\n", streamPathM, streamAddrM,
PLUGIN_NAME_I18N, VERSION);
debug("cIptvProtocolHttp::%s(): requesting: %s", __FUNCTION__, *buffer);
if (!Write(*buffer, (unsigned int)strlen(*buffer))) {
unsigned int len = strlen(*buffer);
debug("cIptvProtocolHttp::%s(): requesting %d: %s", __FUNCTION__, len, *buffer);
if (!Write(*buffer, len)) {
CloseSocket();
return false;
}

View File

@@ -14,7 +14,8 @@ cSidScanner::cSidScanner(void)
: channelIdM(tChannelID::InvalidID),
sidFoundM(false),
nidFoundM(false),
tidFoundM(false)
tidFoundM(false),
isActiveM(false)
{
debug("cSidScanner::%s()", __FUNCTION__);
Set(0x00, 0x00); // PAT
@@ -26,12 +27,6 @@ cSidScanner::~cSidScanner()
debug("cSidScanner::%s()", __FUNCTION__);
}
void cSidScanner::SetStatus(bool onP)
{
debug("cSidScanner::%s(%d)", __FUNCTION__, onP);
cFilter::SetStatus(onP);
}
void cSidScanner::SetChannel(const tChannelID &channelIdP)
{
debug("cSidScanner::%s(%s)", __FUNCTION__, *channelIdP.ToString());
@@ -46,6 +41,8 @@ void cSidScanner::Process(u_short pidP, u_char tidP, const u_char *dataP, int le
int newSid = -1, newNid = -1, newTid = -1;
//debug("cSidScanner::%s()", __FUNCTION__);
if (!isActiveM)
return;
if (channelIdM.Valid()) {
if ((pidP == 0x00) && (tidP == 0x00)) {
debug("cSidScanner::%s(%d, %02X)", __FUNCTION__, pidP, tidP);

View File

@@ -17,17 +17,17 @@ private:
bool sidFoundM;
bool nidFoundM;
bool tidFoundM;
bool isActiveM;
protected:
virtual void Process(u_short pidP, u_char tidP, const u_char *dataP, int lengthP);
virtual void SetStatus(bool onP);
public:
cSidScanner(void);
~cSidScanner();
void SetChannel(const tChannelID &channelIdP);
void Open() { SetStatus(true); }
void Close() { SetStatus(false); }
void Open() { debug("cSidScanner::%s()", __FUNCTION__); isActiveM = true; }
void Close() { debug("cSidScanner::%s()", __FUNCTION__); isActiveM = false; }
};
#endif // __SIDSCANNER_H

View File

@@ -56,12 +56,12 @@ bool cIptvSocket::OpenSocket(const int portP, const bool isUdpP)
ERROR_IF_FUNC(fcntl(socketDescM, F_SETFL, O_NONBLOCK), "fcntl(O_NONBLOCK)",
CloseSocket(), return false);
// Allow multiple sockets to use the same PORT number
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0, "setsockopt(SO_REUSEADDR)",
CloseSocket(), return false);
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0,
"setsockopt(SO_REUSEADDR)", CloseSocket(), return false);
#ifndef __FreeBSD__
// Allow packet information to be fetched
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0, "setsockopt(IP_PKTINFO)",
CloseSocket(), return false);
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0,
"setsockopt(IP_PKTINFO)", CloseSocket(), return false);
#endif // __FreeBSD__
// Bind socket
memset(&sockAddrM, 0, sizeof(sockAddrM));
@@ -69,7 +69,8 @@ bool cIptvSocket::OpenSocket(const int portP, const bool isUdpP)
sockAddrM.sin_port = htons((uint16_t)(portP & 0xFFFF));
sockAddrM.sin_addr.s_addr = htonl(INADDR_ANY);
if (isUdpP)
ERROR_IF_FUNC(bind(socketDescM, (struct sockaddr *)&sockAddrM, sizeof(sockAddrM)) < 0, "bind()", CloseSocket(), return false);
ERROR_IF_FUNC(bind(socketDescM, (struct sockaddr *)&sockAddrM, sizeof(sockAddrM)) < 0,
"bind()", CloseSocket(), return false);
// Update socket port
socketPortM = portP;
}
@@ -99,7 +100,8 @@ bool cIptvSocket::CheckAddress(const char *addrP, in_addr_t *inAddrP)
struct hostent *host = gethostbyname(addrP);
if (!host) {
char tmp[64];
error("gethostbyname() failed: %s is not valid address: %s", addrP, strerror_r(h_errno, tmp, sizeof(tmp)));
error("gethostbyname() failed: %s is not valid address: %s", addrP,
strerror_r(h_errno, tmp, sizeof(tmp)));
return false;
}
*inAddrP = htonl(inet_addr(*host->h_addr_list));
@@ -264,22 +266,22 @@ int cIptvUdpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
else if (len > 3) {
// http://tools.ietf.org/html/rfc3550
// http://tools.ietf.org/html/rfc2250
// version
// Version
unsigned int v = (bufferAddrP[0] >> 6) & 0x03;
// extension bit
// Extension bit
unsigned int x = (bufferAddrP[0] >> 4) & 0x01;
// cscr count
// CSCR count
unsigned int cc = bufferAddrP[0] & 0x0F;
// payload type: MPEG2 TS = 33
// Payload type: MPEG2 TS = 33
//unsigned int pt = bufferAddrP[1] & 0x7F;
// header lenght
// Header lenght
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
// check if extension
// Check if extension
if (x) {
// extension header length
// Extension header length
unsigned int ehl = (((bufferAddrP[headerlen + 2] & 0xFF) << 8) |
(bufferAddrP[headerlen + 3] & 0xFF));
// update header length
// Update header length
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
}
// Check that rtp is version 2 and payload contains multiple of TS packet data

View File

@@ -5,6 +5,7 @@
*
*/
#include <ctype.h>
#include "common.h"
#include "source.h"
@@ -70,7 +71,7 @@ bool cIptvTransponderParameters::Parse(const char *strP)
++data;
if (data && (*data == '=')) {
++data;
switch (*token) {
switch (toupper(*token)) {
case 'S':
sidScanM = (int)strtol(data, (char **)NULL, 10);
found_s = true;
@@ -163,9 +164,8 @@ void cIptvSourceParam::SetData(cChannel *channelP)
void cIptvSourceParam::GetData(cChannel *channelP)
{
debug("cIptvSourceParam::%s(%s)", __FUNCTION__, channelP->Parameters());
dataM.SetTransponderData(channelP->Source(), channelP->Frequency(), dataM.Srate(), itpM.ToString(Source()), true);
dataM.SetId(nidM, tidM, channelP->Sid(), ridM);
*channelP = dataM;
channelP->SetTransponderData(channelP->Source(), channelP->Frequency(), dataM.Srate(), itpM.ToString(Source()), true);
channelP->SetId(nidM, tidM, channelP->Sid(), ridM);
}
cOsdItem *cIptvSourceParam::GetOsdItem(void)

View File

@@ -14,6 +14,7 @@
cIptvStreamer::cIptvStreamer(cRingBufferLinear* ringBufferP, unsigned int packetLenP)
: cThread("IPTV streamer"),
ringBufferM(ringBufferP),
sleepM(),
packetBufferLenM(packetLenP),
protocolM(NULL)
{
@@ -45,8 +46,9 @@ void cIptvStreamer::Action(void)
// Do the thread loop
while (packetBufferM && Running()) {
int length = -1;
if (protocolM)
length = protocolM->Read(packetBufferM, min((unsigned int)ringBufferM->Free(), packetBufferLenM));
unsigned int size = min((unsigned int)ringBufferM->Free(), packetBufferLenM);
if (protocolM && (size > 0))
length = protocolM->Read(packetBufferM, size);
if (length > 0) {
AddStreamerStatistic(length);
if (ringBufferM) {