mirror of
https://github.com/rofafor/vdr-plugin-iptv.git
synced 2023-10-10 11:37:03 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc64c044a1 | ||
|
|
3dcbff0a71 | ||
|
|
5aefd40d65 | ||
|
|
6b0337d078 | ||
|
|
9e6d784aec | ||
|
|
3f3ba2ad1a | ||
|
|
a4b9570ae6 | ||
|
|
767b1bdac7 | ||
|
|
6f648401a4 | ||
|
|
b0995c9a9e | ||
|
|
412af81271 | ||
|
|
43326bb4ea | ||
|
|
555c08bd4d | ||
|
|
5ad3103626 | ||
|
|
a4f0bdf737 | ||
|
|
01b554a2de | ||
|
|
225b0cc297 | ||
|
|
d940e616e3 | ||
|
|
f30817677e | ||
|
|
0c6257e4ca | ||
|
|
0caf746ef4 | ||
|
|
41fe387018 | ||
|
|
2c287ef667 | ||
|
|
d7dfa0aad6 | ||
|
|
72a9f21006 | ||
|
|
d84fd79d14 | ||
|
|
f632650547 | ||
|
|
980aafb206 | ||
|
|
45192e924a | ||
|
|
e4657c1820 |
36
HISTORY
36
HISTORY
@@ -193,3 +193,39 @@ VDR Plugin 'iptv' Revision History
|
|||||||
|
|
||||||
- Updated for vdr-1.7.38.
|
- Updated for vdr-1.7.38.
|
||||||
- Added a new CURL protocol for HTTP/HTTPS.
|
- 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.
|
||||||
|
|
||||||
|
|
||||||
|
==================================
|
||||||
|
VDR Plugin 'iptv' Revision History
|
||||||
|
==================================
|
||||||
|
|
||||||
|
2013-04-01: Version 2.0.0
|
||||||
|
|
||||||
|
- Updated for vdr-2.0.0.
|
||||||
|
- Enabled I/O throttling and tweaked buffer timeouts.
|
||||||
|
- Fixed a nasty network byte order bug.
|
||||||
|
- Fixed and refactored the section filtering code.
|
||||||
|
- Fixed a possible crash in the file protocol.
|
||||||
|
|
||||||
|
2014-01-10: Version 2.0.1
|
||||||
|
|
||||||
|
- Added missing CURL timeouts.
|
||||||
|
- Improved section id scanner.
|
||||||
|
- Added support for cDevice::IsTunedToTransponder() and
|
||||||
|
cDevice::GetCurrentlyTunedTransponder().
|
||||||
|
- Fixed a memory leak and some issues reported by scan-build.
|
||||||
|
|
||||||
|
2014-01-18: Version 2.0.2
|
||||||
|
|
||||||
|
- Made devices to shutdown already in cPluginManager::Stop()
|
||||||
|
to prevent possible crashes while VDR shutdown.
|
||||||
|
|
||||||
|
2014-03-09: Version 2.0.3
|
||||||
|
|
||||||
|
- Fixed installation target bugs (Thanks to Alexander Grothe).
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -28,10 +28,12 @@ GITTAG = $(shell git describe --always 2>/dev/null)
|
|||||||
### The directory environment:
|
### The directory environment:
|
||||||
|
|
||||||
# Use package data if installed...otherwise assume we're under the VDR source directory:
|
# Use package data if installed...otherwise assume we're under the VDR source directory:
|
||||||
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
|
PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
|
||||||
LIBDIR = $(call PKGCFG,libdir)
|
LIBDIR = $(call PKGCFG,libdir)
|
||||||
LOCDIR = $(call PKGCFG,locdir)
|
LOCDIR = $(call PKGCFG,locdir)
|
||||||
PLGCFG = $(call PKGCFG,plgcfg)
|
PLGCFG = $(call PKGCFG,plgcfg)
|
||||||
|
RESDIR = $(call PKGCFG,resdir)
|
||||||
|
CFGDIR = $(call PKGCFG,configdir)
|
||||||
#
|
#
|
||||||
TMPDIR ?= /tmp
|
TMPDIR ?= /tmp
|
||||||
|
|
||||||
@@ -144,7 +146,12 @@ endif
|
|||||||
install-lib: $(SOFILE)
|
install-lib: $(SOFILE)
|
||||||
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
|
||||||
|
|
||||||
install: install-lib install-i18n
|
install-conf:
|
||||||
|
@mkdir -p $(DESTDIR)$(CFGDIR)/plugins/$(PLUGIN)
|
||||||
|
@mkdir -p $(DESTDIR)$(RESDIR)/plugins/$(PLUGIN)
|
||||||
|
@cp -pn $(PLUGIN)/* $(DESTDIR)$(RESDIR)/plugins/$(PLUGIN)/
|
||||||
|
|
||||||
|
install: install-lib install-i18n install-conf
|
||||||
|
|
||||||
dist: $(I18Npo) clean
|
dist: $(I18Npo) clean
|
||||||
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
@-rm -rf $(TMPDIR)/$(ARCHIVE)
|
||||||
|
|||||||
28
README
28
README
@@ -41,14 +41,8 @@ MP3 radio streams, mms video streams and so on.
|
|||||||
|
|
||||||
Installation:
|
Installation:
|
||||||
|
|
||||||
cd /put/your/path/here/VDR/PLUGINS/src
|
|
||||||
tar -xzf /put/your/path/here/vdr-iptv-X.Y.Z.tgz
|
tar -xzf /put/your/path/here/vdr-iptv-X.Y.Z.tgz
|
||||||
ln -s iptv-X.Y.Z iptv
|
make -C iptv-X.Y.Z install
|
||||||
cd /put/your/path/here/VDR
|
|
||||||
cp -R PLUGINS/src/iptv/iptv /path/to/vdrresource/plugins/
|
|
||||||
make
|
|
||||||
make plugins
|
|
||||||
./vdr -P iptv
|
|
||||||
|
|
||||||
Setup menu:
|
Setup menu:
|
||||||
|
|
||||||
@@ -89,11 +83,11 @@ Configuration:
|
|||||||
|
|
||||||
- channels.conf
|
- 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
|
TV6;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:30:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0
|
TV5;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: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
|
TV4;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: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
|
TV3;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: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
|
TV2;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
|
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")
|
| | | | | | Source type ("I")
|
||||||
@@ -171,8 +165,7 @@ Notes:
|
|||||||
"disable_ca_updates" patch to the VDR in order to get rid of "Channel not
|
"disable_ca_updates" patch to the VDR in order to get rid of "Channel not
|
||||||
available" messages.
|
available" messages.
|
||||||
|
|
||||||
- EIT scanning functionality can be disabled for all IPTV channels by applying
|
- EIT scanning functionality is disabled by default.
|
||||||
the "disable_eitscan" patch to the VDR.
|
|
||||||
|
|
||||||
- Section id and pid scanners should be disabled after the correct data is
|
- Section id and pid scanners should be disabled after the correct data is
|
||||||
found. This can be made via VDR's channel editor.
|
found. This can be made via VDR's channel editor.
|
||||||
@@ -186,7 +179,12 @@ Notes:
|
|||||||
netrc configuration file for authentication:
|
netrc configuration file for authentication:
|
||||||
$(CONFDIR)/iptv/netrc
|
$(CONFDIR)/iptv/netrc
|
||||||
|
|
||||||
- CURL implementation
|
- You can quite easily figure out correct DVB triplet values by using the
|
||||||
|
multicat and dvbsnoop tools:
|
||||||
|
$ multicat -u -d 1620000000 @127.0.0.1:1234 /tmp/video.ts
|
||||||
|
$ dvbsnoop -s ts -if /tmp/video.ts -tssubdecode -hexdumpbuffer 0x12 | \
|
||||||
|
grep -m1 -A8 Service_ID | grep _ID
|
||||||
|
|
||||||
Acknowledgements:
|
Acknowledgements:
|
||||||
|
|
||||||
- The IPTV section filtering code is derived from Linux kernel.
|
- The IPTV section filtering code is derived from Linux kernel.
|
||||||
|
|||||||
45
common.c
45
common.c
@@ -5,44 +5,45 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <vdr/tools.h>
|
#include <vdr/tools.h>
|
||||||
#include "common.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;
|
return 0;
|
||||||
|
|
||||||
if (tsp[3] & 0x20) { // adaptation field?
|
if (bufP[3] & 0x20) { // adaptation field?
|
||||||
if (tsp[4] > 183) // corrupted data?
|
if (bufP[4] > 183) // corrupted data?
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return (uint8_t)((184 - 1) - tsp[4]);
|
return (uint8_t)((184 - 1) - bufP[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 184;
|
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) {
|
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 section_filter_table[i].tag;
|
||||||
}
|
}
|
||||||
return "---";
|
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
|
// Wait for data
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = usecs;
|
tv.tv_usec = usecsP;
|
||||||
// Use select
|
// Use select
|
||||||
fd_set infd;
|
fd_set infd;
|
||||||
fd_set outfd;
|
fd_set outfd;
|
||||||
@@ -50,17 +51,28 @@ int select_single_desc(int descriptor, const int usecs, const bool selectWrite)
|
|||||||
FD_ZERO(&infd);
|
FD_ZERO(&infd);
|
||||||
FD_ZERO(&outfd);
|
FD_ZERO(&outfd);
|
||||||
FD_ZERO(&errfd);
|
FD_ZERO(&errfd);
|
||||||
FD_SET(descriptor, &errfd);
|
FD_SET(descriptorP, &errfd);
|
||||||
if (selectWrite)
|
if (selectWriteP)
|
||||||
FD_SET(descriptor, &outfd);
|
FD_SET(descriptorP, &outfd);
|
||||||
else
|
else
|
||||||
FD_SET(descriptor, &infd);
|
FD_SET(descriptorP, &infd);
|
||||||
int retval = select(descriptor + 1, &infd, &outfd, &errfd, &tv);
|
int retval = select(descriptorP + 1, &infd, &outfd, &errfd, &tv);
|
||||||
// Check if error
|
// Check if error
|
||||||
ERROR_IF_RET(retval < 0, "select()", return retval);
|
ERROR_IF_RET(retval < 0, "select()", return retval);
|
||||||
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] =
|
const section_filter_table_type section_filter_table[SECTION_FILTER_TABLE_SIZE] =
|
||||||
{
|
{
|
||||||
/* description tag pid tid mask */
|
/* 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("EIT (0x6X)"), "EIT", 0x12, 0x60, 0xF0},
|
||||||
{trNOOP("TDT (0x70)"), "TDT", 0x14, 0x70, 0xFF},
|
{trNOOP("TDT (0x70)"), "TDT", 0x14, 0x70, 0xFF},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
12
common.h
12
common.h
@@ -44,7 +44,8 @@
|
|||||||
do { \
|
do { \
|
||||||
if (exp) { \
|
if (exp) { \
|
||||||
char tmp[64]; \
|
char tmp[64]; \
|
||||||
error(errstr": %s", strerror_r(errno, tmp, sizeof(tmp))); \
|
error("[%s,%d]: "errstr": %s", __FILE__, __LINE__, \
|
||||||
|
strerror_r(errno, tmp, sizeof(tmp))); \
|
||||||
func; \
|
func; \
|
||||||
ret; \
|
ret; \
|
||||||
} \
|
} \
|
||||||
@@ -66,10 +67,11 @@
|
|||||||
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
uint16_t ts_pid(const uint8_t *buf);
|
uint16_t ts_pid(const uint8_t *bufP);
|
||||||
uint8_t payload(const uint8_t *tsp);
|
uint8_t payload(const uint8_t *bufP);
|
||||||
const char *id_pid(const u_short Pid);
|
const char *id_pid(const u_short pidP);
|
||||||
int select_single_desc(int descriptor, const int usecs, const bool selectWrite);
|
int select_single_desc(int descriptorP, const int usecsP, const bool selectWriteP);
|
||||||
|
cString ChangeCase(const cString &strP, bool upperP);
|
||||||
|
|
||||||
struct section_filter_table_type {
|
struct section_filter_table_type {
|
||||||
const char *description;
|
const char *description;
|
||||||
|
|||||||
7
config.c
7
config.c
@@ -19,6 +19,7 @@ cIptvConfig::cIptvConfig(void)
|
|||||||
for (unsigned int i = 0; i < ARRAY_SIZE(disabledFiltersM); ++i)
|
for (unsigned int i = 0; i < ARRAY_SIZE(disabledFiltersM); ++i)
|
||||||
disabledFiltersM[i] = -1;
|
disabledFiltersM[i] = -1;
|
||||||
memset(configDirectoryM, 0, sizeof(configDirectoryM));
|
memset(configDirectoryM, 0, sizeof(configDirectoryM));
|
||||||
|
memset(resourceDirectoryM, 0, sizeof(resourceDirectoryM));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int cIptvConfig::GetDisabledFiltersCount(void) const
|
unsigned int cIptvConfig::GetDisabledFiltersCount(void) const
|
||||||
@@ -45,3 +46,9 @@ void cIptvConfig::SetConfigDirectory(const char *directoryP)
|
|||||||
debug("cIptvConfig::%s(%s)", __FUNCTION__, directoryP);
|
debug("cIptvConfig::%s(%s)", __FUNCTION__, directoryP);
|
||||||
ERROR_IF(!realpath(directoryP, configDirectoryM), "Cannot canonicalize configuration directory");
|
ERROR_IF(!realpath(directoryP, configDirectoryM), "Cannot canonicalize configuration directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cIptvConfig::SetResourceDirectory(const char *directoryP)
|
||||||
|
{
|
||||||
|
debug("cIptvConfig::%s(%s)", __FUNCTION__, directoryP);
|
||||||
|
ERROR_IF(!realpath(directoryP, resourceDirectoryM), "Cannot canonicalize resource directory");
|
||||||
|
}
|
||||||
|
|||||||
3
config.h
3
config.h
@@ -21,6 +21,7 @@ private:
|
|||||||
unsigned int sectionFilteringM;
|
unsigned int sectionFilteringM;
|
||||||
int disabledFiltersM[SECTION_FILTER_TABLE_SIZE];
|
int disabledFiltersM[SECTION_FILTER_TABLE_SIZE];
|
||||||
char configDirectoryM[PATH_MAX];
|
char configDirectoryM[PATH_MAX];
|
||||||
|
char resourceDirectoryM[PATH_MAX];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cIptvConfig();
|
cIptvConfig();
|
||||||
@@ -30,6 +31,7 @@ public:
|
|||||||
unsigned int GetUseBytes(void) const { return useBytesM; }
|
unsigned int GetUseBytes(void) const { return useBytesM; }
|
||||||
unsigned int GetSectionFiltering(void) const { return sectionFilteringM; }
|
unsigned int GetSectionFiltering(void) const { return sectionFilteringM; }
|
||||||
const char *GetConfigDirectory(void) const { return configDirectoryM; }
|
const char *GetConfigDirectory(void) const { return configDirectoryM; }
|
||||||
|
const char *GetResourceDirectory(void) const { return resourceDirectoryM; }
|
||||||
unsigned int GetDisabledFiltersCount(void) const;
|
unsigned int GetDisabledFiltersCount(void) const;
|
||||||
int GetDisabledFilters(unsigned int indexP) const;
|
int GetDisabledFilters(unsigned int indexP) const;
|
||||||
void SetTsBufferSize(unsigned int sizeP) { tsBufferSizeM = sizeP; }
|
void SetTsBufferSize(unsigned int sizeP) { tsBufferSizeM = sizeP; }
|
||||||
@@ -39,6 +41,7 @@ public:
|
|||||||
void SetSectionFiltering(unsigned int onOffP) { sectionFilteringM = onOffP; }
|
void SetSectionFiltering(unsigned int onOffP) { sectionFilteringM = onOffP; }
|
||||||
void SetDisabledFilters(unsigned int indexP, int numberP);
|
void SetDisabledFilters(unsigned int indexP, int numberP);
|
||||||
void SetConfigDirectory(const char *directoryP);
|
void SetConfigDirectory(const char *directoryP);
|
||||||
|
void SetResourceDirectory(const char *directoryP);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern cIptvConfig IptvConfig;
|
extern cIptvConfig IptvConfig;
|
||||||
|
|||||||
216
device.c
216
device.c
@@ -20,30 +20,30 @@ cIptvDevice::cIptvDevice(unsigned int indexP)
|
|||||||
isOpenDvrM(false),
|
isOpenDvrM(false),
|
||||||
sidScanEnabledM(false),
|
sidScanEnabledM(false),
|
||||||
pidScanEnabledM(false),
|
pidScanEnabledM(false),
|
||||||
channelIdM(tChannelID::InvalidID)
|
channelM()
|
||||||
{
|
{
|
||||||
unsigned int bufsize = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize());
|
unsigned int bufsize = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize());
|
||||||
bufsize -= (bufsize % TS_SIZE);
|
bufsize -= (bufsize % TS_SIZE);
|
||||||
isyslog("creating IPTV device %d (CardIndex=%d)", deviceIndexM, CardIndex());
|
isyslog("creating IPTV device %d (CardIndex=%d)", deviceIndexM, CardIndex());
|
||||||
tsBufferM = new cRingBufferLinear(bufsize + 1, TS_SIZE, false,
|
tsBufferM = new cRingBufferLinear(bufsize + 1, TS_SIZE, false,
|
||||||
*cString::sprintf("IPTV %d", deviceIndexM));
|
*cString::sprintf("IPTV TS %d", deviceIndexM));
|
||||||
tsBufferM->SetTimeouts(10, 10);
|
if (tsBufferM) {
|
||||||
|
tsBufferM->SetTimeouts(100, 100);
|
||||||
|
tsBufferM->SetIoThrottle();
|
||||||
|
pIptvStreamerM = new cIptvStreamer(*this, tsBufferM->Free());
|
||||||
|
}
|
||||||
ResetBuffering();
|
ResetBuffering();
|
||||||
pUdpProtocolM = new cIptvProtocolUdp();
|
pUdpProtocolM = new cIptvProtocolUdp();
|
||||||
pCurlProtocolM = new cIptvProtocolCurl();
|
pCurlProtocolM = new cIptvProtocolCurl();
|
||||||
pHttpProtocolM = new cIptvProtocolHttp();
|
pHttpProtocolM = new cIptvProtocolHttp();
|
||||||
pFileProtocolM = new cIptvProtocolFile();
|
pFileProtocolM = new cIptvProtocolFile();
|
||||||
pExtProtocolM = new cIptvProtocolExt();
|
pExtProtocolM = new cIptvProtocolExt();
|
||||||
pIptvStreamerM = new cIptvStreamer(tsBufferM, (100 * TS_SIZE));
|
|
||||||
pPidScannerM = new cPidScanner();
|
pPidScannerM = new cPidScanner();
|
||||||
// Initialize filter pointers
|
|
||||||
memset(secFiltersM, 0, sizeof(secFiltersM));
|
|
||||||
// Start section handler for iptv device
|
// Start section handler for iptv device
|
||||||
|
pIptvSectionM = new cIptvSectionFilterHandler(deviceIndexM, bufsize + 1);
|
||||||
StartSectionHandler();
|
StartSectionHandler();
|
||||||
// Sid scanner must be created after the section handler
|
// Sid scanner must be created after the section handler
|
||||||
pSidScannerM = new cSidScanner();
|
AttachFilter(pSidScannerM = new cSidScanner());
|
||||||
if (pSidScannerM)
|
|
||||||
AttachFilter(pSidScannerM);
|
|
||||||
// Check if dvr fifo exists
|
// Check if dvr fifo exists
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
cString filename = cString::sprintf(IPTV_DVR_FILENAME, deviceIndexM);
|
cString filename = cString::sprintf(IPTV_DVR_FILENAME, deviceIndexM);
|
||||||
@@ -60,22 +60,16 @@ cIptvDevice::~cIptvDevice()
|
|||||||
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
// Stop section handler of iptv device
|
// Stop section handler of iptv device
|
||||||
StopSectionHandler();
|
StopSectionHandler();
|
||||||
DELETE_POINTER(pIptvStreamerM);
|
DELETE_POINTER(pIptvSectionM);
|
||||||
DELETE_POINTER(pUdpProtocolM);
|
DELETE_POINTER(pSidScannerM);
|
||||||
DELETE_POINTER(pCurlProtocolM);
|
|
||||||
DELETE_POINTER(pHttpProtocolM);
|
|
||||||
DELETE_POINTER(pFileProtocolM);
|
|
||||||
DELETE_POINTER(pExtProtocolM);
|
|
||||||
DELETE_POINTER(tsBufferM);
|
|
||||||
DELETE_POINTER(pPidScannerM);
|
DELETE_POINTER(pPidScannerM);
|
||||||
// Detach and destroy sid filter
|
DELETE_POINTER(pIptvStreamerM);
|
||||||
if (pSidScannerM) {
|
DELETE_POINTER(pExtProtocolM);
|
||||||
Detach(pSidScannerM);
|
DELETE_POINTER(pFileProtocolM);
|
||||||
DELETE_POINTER(pSidScannerM);
|
DELETE_POINTER(pHttpProtocolM);
|
||||||
}
|
DELETE_POINTER(pCurlProtocolM);
|
||||||
// Destroy all filters
|
DELETE_POINTER(pUdpProtocolM);
|
||||||
for (int i = 0; i < eMaxSecFilterCount; ++i)
|
DELETE_POINTER(tsBufferM);
|
||||||
DeleteFilter(i);
|
|
||||||
// Close dvr fifo
|
// Close dvr fifo
|
||||||
if (dvrFdM >= 0) {
|
if (dvrFdM >= 0) {
|
||||||
int fd = dvrFdM;
|
int fd = dvrFdM;
|
||||||
@@ -97,6 +91,15 @@ bool cIptvDevice::Initialize(unsigned int deviceCountP)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cIptvDevice::Shutdown(void)
|
||||||
|
{
|
||||||
|
debug("cIptvDevice::%s()", __FUNCTION__);
|
||||||
|
for (int i = 0; i < IPTV_MAX_DEVICES; ++i) {
|
||||||
|
if (IptvDevicesS[i])
|
||||||
|
IptvDevicesS[i]->CloseDvr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int cIptvDevice::Count(void)
|
unsigned int cIptvDevice::Count(void)
|
||||||
{
|
{
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
@@ -112,7 +115,7 @@ cIptvDevice *cIptvDevice::GetIptvDevice(int cardIndexP)
|
|||||||
{
|
{
|
||||||
//debug("cIptvDevice::%s(%d)", __FUNCTION__, cardIndexP);
|
//debug("cIptvDevice::%s(%d)", __FUNCTION__, cardIndexP);
|
||||||
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
|
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
|
||||||
if ((IptvDevicesS[i] != NULL) && (IptvDevicesS[i]->CardIndex() == cardIndexP)) {
|
if (IptvDevicesS[i] && (IptvDevicesS[i]->CardIndex() == cardIndexP)) {
|
||||||
//debug("cIptvDevice::%s(%d): found!", __FUNCTION__, cardIndexP);
|
//debug("cIptvDevice::%s(%d): found!", __FUNCTION__, cardIndexP);
|
||||||
return IptvDevicesS[i];
|
return IptvDevicesS[i];
|
||||||
}
|
}
|
||||||
@@ -140,19 +143,7 @@ cString cIptvDevice::GetPidsInformation(void)
|
|||||||
cString cIptvDevice::GetFiltersInformation(void)
|
cString cIptvDevice::GetFiltersInformation(void)
|
||||||
{
|
{
|
||||||
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
unsigned int count = 0;
|
return cString::sprintf("Active section filters:\n%s", pIptvSectionM ? *pIptvSectionM->GetInformation() : "");
|
||||||
cString s("Active section filters:\n");
|
|
||||||
// loop through active section filters
|
|
||||||
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
|
||||||
if (secFiltersM[i]) {
|
|
||||||
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
|
|
||||||
*secFiltersM[i]->GetSectionStatistic(), secFiltersM[i]->GetPid(),
|
|
||||||
id_pid(secFiltersM[i]->GetPid()));
|
|
||||||
if (++count > IPTV_STATS_ACTIVE_FILTERS_COUNT)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cString cIptvDevice::GetInformation(unsigned int pageP)
|
cString cIptvDevice::GetInformation(unsigned int pageP)
|
||||||
@@ -187,7 +178,7 @@ cString cIptvDevice::GetInformation(unsigned int pageP)
|
|||||||
|
|
||||||
cString cIptvDevice::DeviceType(void) const
|
cString cIptvDevice::DeviceType(void) const
|
||||||
{
|
{
|
||||||
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
return "IPTV";
|
return "IPTV";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +223,7 @@ bool cIptvDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool
|
|||||||
if (channelP && ProvidesTransponder(channelP)) {
|
if (channelP && ProvidesTransponder(channelP)) {
|
||||||
result = hasPriority;
|
result = hasPriority;
|
||||||
if (Receiving()) {
|
if (Receiving()) {
|
||||||
if (channelP->GetChannelID() == channelIdM)
|
if (channelP->GetChannelID() == channelM.GetChannelID())
|
||||||
result = true;
|
result = true;
|
||||||
else
|
else
|
||||||
needsDetachReceivers = Receiving();
|
needsDetachReceivers = Receiving();
|
||||||
@@ -253,6 +244,21 @@ int cIptvDevice::NumProvidedSystems(void) const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cChannel *cIptvDevice::GetCurrentlyTunedTransponder(void) const
|
||||||
|
{
|
||||||
|
return &channelM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvDevice::IsTunedToTransponder(const cChannel *channelP) const
|
||||||
|
{
|
||||||
|
return channelP ? (channelP->GetChannelID() == channelM.GetChannelID()) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvDevice::MaySwitchTransponder(const cChannel *channelP) const
|
||||||
|
{
|
||||||
|
return cDevice::MaySwitchTransponder(channelP);
|
||||||
|
}
|
||||||
|
|
||||||
bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
|
bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
|
||||||
{
|
{
|
||||||
cIptvProtocolIf *protocol;
|
cIptvProtocolIf *protocol;
|
||||||
@@ -288,11 +294,11 @@ bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
|
|||||||
sidScanEnabledM = itp.SidScan() ? true : false;
|
sidScanEnabledM = itp.SidScan() ? true : false;
|
||||||
pidScanEnabledM = itp.PidScan() ? true : false;
|
pidScanEnabledM = itp.PidScan() ? true : false;
|
||||||
if (pIptvStreamerM->Set(itp.Address(), itp.Parameter(), deviceIndexM, protocol)) {
|
if (pIptvStreamerM->Set(itp.Address(), itp.Parameter(), deviceIndexM, protocol)) {
|
||||||
channelIdM = channelP->GetChannelID();
|
channelM = *channelP;
|
||||||
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
|
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
|
||||||
pSidScannerM->SetChannel(channelIdM);
|
pSidScannerM->SetChannel(channelM.GetChannelID());
|
||||||
if (pidScanEnabledM && pPidScannerM)
|
if (pidScanEnabledM && pPidScannerM)
|
||||||
pPidScannerM->SetChannel(channelIdM);
|
pPidScannerM->SetChannel(channelM.GetChannelID());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -303,80 +309,19 @@ bool cIptvDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::DeleteFilter(unsigned int indexP)
|
|
||||||
{
|
|
||||||
if ((indexP < eMaxSecFilterCount) && secFiltersM[indexP]) {
|
|
||||||
//debug("cIptvDevice::%s(%d): index=%d", __FUNCTION__, deviceIndexM, indexP);
|
|
||||||
cIptvSectionFilter *tmp = secFiltersM[indexP];
|
|
||||||
secFiltersM[indexP] = NULL;
|
|
||||||
delete tmp;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cIptvDevice::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const
|
|
||||||
{
|
|
||||||
//debug("cIptvDevice::%s(%d): pid=%d tid=%02X mask=%02X", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
|
|
||||||
// loop through section filter table
|
|
||||||
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
|
|
||||||
int index = IptvConfig.GetDisabledFilters(i);
|
|
||||||
// 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)) {
|
|
||||||
//debug("cIptvDevice::%s(%d): found %s", __FUNCTION__, deviceIndexM, section_filter_table[index].description);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cIptvDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
|
int cIptvDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
|
||||||
{
|
{
|
||||||
// Check if disabled by user
|
//debug("cIptvDevice::%s(%d): pid=%d tid=%d mask=%d", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
|
||||||
if (!IptvConfig.GetSectionFiltering())
|
if (pIptvSectionM && IptvConfig.GetSectionFiltering())
|
||||||
return -1;
|
return pIptvSectionM->Open(pidP, tidP, maskP);
|
||||||
// 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]) {
|
|
||||||
//debug("cIptvDevice::%s(%d): pid=%d tid=%02X mask=%02X index=%d", __FUNCTION__, deviceIndexM, pidP, tidP, maskP, i);
|
|
||||||
secFiltersM[i] = new cIptvSectionFilter(deviceIndexM, pidP, tidP, maskP);
|
|
||||||
if (secFiltersM[i])
|
|
||||||
return i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No free filter slot found
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cIptvDevice::ReadFilter(int handleP, void *bufferP, size_t lengthP)
|
|
||||||
{
|
|
||||||
// Lock
|
|
||||||
cMutexLock MutexLock(&mutexM);
|
|
||||||
// ... and load
|
|
||||||
if (secFiltersM[handleP]) {
|
|
||||||
return secFiltersM[handleP]->Read(bufferP, lengthP);
|
|
||||||
//debug("cIptvDevice::%s(%d): handle=%d length=%d", __FUNCTION__, deviceIndexM, handleP, lengthP);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cIptvDevice::CloseFilter(int handleP)
|
void cIptvDevice::CloseFilter(int handleP)
|
||||||
{
|
{
|
||||||
// Lock
|
//debug("cIptvDevice::%s(%d): handle=%d", __FUNCTION__, deviceIndexM, handleP);
|
||||||
cMutexLock MutexLock(&mutexM);
|
if (pIptvSectionM)
|
||||||
// ... and load
|
pIptvSectionM->Close(handleP);
|
||||||
if (secFiltersM[handleP]) {
|
|
||||||
//debug("cIptvDevice::%s(%d): handle=%d", __FUNCTION__, deviceIndexM, handleP);
|
|
||||||
DeleteFilter(handleP);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::OpenDvr(void)
|
bool cIptvDevice::OpenDvr(void)
|
||||||
@@ -389,6 +334,8 @@ bool cIptvDevice::OpenDvr(void)
|
|||||||
pIptvStreamerM->Open();
|
pIptvStreamerM->Open();
|
||||||
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
|
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
|
||||||
pSidScannerM->Open();
|
pSidScannerM->Open();
|
||||||
|
if (pidScanEnabledM && pPidScannerM)
|
||||||
|
pPidScannerM->Open();
|
||||||
isOpenDvrM = true;
|
isOpenDvrM = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -396,7 +343,9 @@ bool cIptvDevice::OpenDvr(void)
|
|||||||
void cIptvDevice::CloseDvr(void)
|
void cIptvDevice::CloseDvr(void)
|
||||||
{
|
{
|
||||||
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
|
if (pidScanEnabledM && pPidScannerM)
|
||||||
|
pPidScannerM->Close();
|
||||||
|
if (sidScanEnabledM && pSidScannerM)
|
||||||
pSidScannerM->Close();
|
pSidScannerM->Close();
|
||||||
if (pIptvStreamerM)
|
if (pIptvStreamerM)
|
||||||
pIptvStreamerM->Close();
|
pIptvStreamerM->Close();
|
||||||
@@ -418,7 +367,7 @@ bool cIptvDevice::HasInternalCam(void)
|
|||||||
void cIptvDevice::ResetBuffering(void)
|
void cIptvDevice::ResetBuffering(void)
|
||||||
{
|
{
|
||||||
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
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()) *
|
tsBufferPrefillM = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize()) *
|
||||||
IptvConfig.GetTsBufferPrefillRatio() / 100;
|
IptvConfig.GetTsBufferPrefillRatio() / 100;
|
||||||
tsBufferPrefillM -= (tsBufferPrefillM % TS_SIZE);
|
tsBufferPrefillM -= (tsBufferPrefillM % TS_SIZE);
|
||||||
@@ -434,10 +383,36 @@ bool cIptvDevice::IsBuffering(void) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cIptvDevice::WriteData(uchar *bufferP, int lengthP)
|
||||||
|
{
|
||||||
|
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
int len;
|
||||||
|
// Send data to dvr fifo
|
||||||
|
if (dvrFdM >= 0)
|
||||||
|
len = write(dvrFdM, bufferP, lengthP);
|
||||||
|
// Fill up TS buffer
|
||||||
|
if (tsBufferM) {
|
||||||
|
len = tsBufferM->Put(bufferP, lengthP);
|
||||||
|
if (len != lengthP)
|
||||||
|
tsBufferM->ReportOverflow(lengthP - len);
|
||||||
|
}
|
||||||
|
// Filter the sections
|
||||||
|
if (pIptvSectionM && IptvConfig.GetSectionFiltering())
|
||||||
|
pIptvSectionM->Write(bufferP, lengthP);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cIptvDevice::CheckData(void)
|
||||||
|
{
|
||||||
|
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
if (tsBufferM)
|
||||||
|
return (unsigned int)tsBufferM->Free();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool cIptvDevice::GetTSPacket(uchar *&Data)
|
bool cIptvDevice::GetTSPacket(uchar *&Data)
|
||||||
{
|
{
|
||||||
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
if (tsBufferM && !IsBuffering()) {
|
if (isOpenDvrM && tsBufferM && !IsBuffering()) {
|
||||||
if (isPacketDeliveredM) {
|
if (isPacketDeliveredM) {
|
||||||
tsBufferM->Del(TS_SIZE);
|
tsBufferM->Del(TS_SIZE);
|
||||||
isPacketDeliveredM = false;
|
isPacketDeliveredM = false;
|
||||||
@@ -455,26 +430,13 @@ bool cIptvDevice::GetTSPacket(uchar *&Data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tsBufferM->Del(Count);
|
tsBufferM->Del(Count);
|
||||||
error("Skipped %d bytes to sync on TS packet\n", Count);
|
error("Skipped %d bytes to sync on TS packet", Count);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isPacketDeliveredM = true;
|
isPacketDeliveredM = true;
|
||||||
Data = p;
|
Data = p;
|
||||||
// Update pid statistics
|
// Update pid statistics
|
||||||
AddPidStatistic(ts_pid(p), payload(p));
|
AddPidStatistic(ts_pid(p), payload(p));
|
||||||
// Send data also to dvr fifo
|
|
||||||
if (dvrFdM >= 0)
|
|
||||||
Count = (int)write(dvrFdM, p, TS_SIZE);
|
|
||||||
// Analyze incomplete streams with built-in pid analyzer
|
|
||||||
if (pidScanEnabledM && pPidScannerM)
|
|
||||||
pPidScannerM->Process(p);
|
|
||||||
// Lock
|
|
||||||
cMutexLock MutexLock(&mutexM);
|
|
||||||
// Run the data through all filters
|
|
||||||
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
|
||||||
if (secFiltersM[i])
|
|
||||||
secFiltersM[i]->Process(p);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
device.h
23
device.h
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <vdr/device.h>
|
#include <vdr/device.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "deviceif.h"
|
||||||
#include "protocoludp.h"
|
#include "protocoludp.h"
|
||||||
#include "protocolcurl.h"
|
#include "protocolcurl.h"
|
||||||
#include "protocolhttp.h"
|
#include "protocolhttp.h"
|
||||||
@@ -21,19 +22,17 @@
|
|||||||
#include "sidscanner.h"
|
#include "sidscanner.h"
|
||||||
#include "statistics.h"
|
#include "statistics.h"
|
||||||
|
|
||||||
class cIptvDevice : public cDevice, public cIptvPidStatistics, public cIptvBufferStatistics {
|
class cIptvDevice : public cDevice, public cIptvPidStatistics, public cIptvBufferStatistics, public cIptvDeviceIf {
|
||||||
// static ones
|
// static ones
|
||||||
public:
|
public:
|
||||||
static unsigned int deviceCount;
|
static unsigned int deviceCount;
|
||||||
static bool Initialize(unsigned int DeviceCount);
|
static bool Initialize(unsigned int DeviceCount);
|
||||||
|
static void Shutdown(void);
|
||||||
static unsigned int Count(void);
|
static unsigned int Count(void);
|
||||||
static cIptvDevice *GetIptvDevice(int CardIndex);
|
static cIptvDevice *GetIptvDevice(int CardIndex);
|
||||||
|
|
||||||
// private parts
|
// private parts
|
||||||
private:
|
private:
|
||||||
enum {
|
|
||||||
eMaxSecFilterCount = 32
|
|
||||||
};
|
|
||||||
unsigned int deviceIndexM;
|
unsigned int deviceIndexM;
|
||||||
int dvrFdM;
|
int dvrFdM;
|
||||||
bool isPacketDeliveredM;
|
bool isPacketDeliveredM;
|
||||||
@@ -42,17 +41,17 @@ private:
|
|||||||
bool pidScanEnabledM;
|
bool pidScanEnabledM;
|
||||||
cRingBufferLinear *tsBufferM;
|
cRingBufferLinear *tsBufferM;
|
||||||
mutable int tsBufferPrefillM;
|
mutable int tsBufferPrefillM;
|
||||||
tChannelID channelIdM;
|
cChannel channelM;
|
||||||
cIptvProtocolUdp *pUdpProtocolM;
|
cIptvProtocolUdp *pUdpProtocolM;
|
||||||
cIptvProtocolCurl *pCurlProtocolM;
|
cIptvProtocolCurl *pCurlProtocolM;
|
||||||
cIptvProtocolHttp *pHttpProtocolM;
|
cIptvProtocolHttp *pHttpProtocolM;
|
||||||
cIptvProtocolFile *pFileProtocolM;
|
cIptvProtocolFile *pFileProtocolM;
|
||||||
cIptvProtocolExt *pExtProtocolM;
|
cIptvProtocolExt *pExtProtocolM;
|
||||||
cIptvStreamer *pIptvStreamerM;
|
cIptvStreamer *pIptvStreamerM;
|
||||||
|
cIptvSectionFilterHandler *pIptvSectionM;
|
||||||
cPidScanner *pPidScannerM;
|
cPidScanner *pPidScannerM;
|
||||||
cSidScanner *pSidScannerM;
|
cSidScanner *pSidScannerM;
|
||||||
cMutex mutexM;
|
cMutex mutexM;
|
||||||
cIptvSectionFilter *secFiltersM[eMaxSecFilterCount];
|
|
||||||
|
|
||||||
// constructor & destructor
|
// constructor & destructor
|
||||||
public:
|
public:
|
||||||
@@ -74,8 +73,6 @@ private:
|
|||||||
private:
|
private:
|
||||||
void ResetBuffering(void);
|
void ResetBuffering(void);
|
||||||
bool IsBuffering(void) const;
|
bool IsBuffering(void) const;
|
||||||
bool DeleteFilter(unsigned int indexP);
|
|
||||||
bool IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const;
|
|
||||||
|
|
||||||
// for channel info
|
// for channel info
|
||||||
public:
|
public:
|
||||||
@@ -91,6 +88,10 @@ public:
|
|||||||
virtual bool ProvidesChannel(const cChannel *channelP, int priorityP = -1, bool *needsDetachReceiversP = NULL) const;
|
virtual bool ProvidesChannel(const cChannel *channelP, int priorityP = -1, bool *needsDetachReceiversP = NULL) const;
|
||||||
virtual bool ProvidesEIT(void) const;
|
virtual bool ProvidesEIT(void) const;
|
||||||
virtual int NumProvidedSystems(void) const;
|
virtual int NumProvidedSystems(void) const;
|
||||||
|
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
|
||||||
|
virtual bool IsTunedToTransponder(const cChannel *channelP) const;
|
||||||
|
virtual bool MaySwitchTransponder(const cChannel *channelP) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool SetChannelDevice(const cChannel *channelP, bool liveViewP);
|
virtual bool SetChannelDevice(const cChannel *channelP, bool liveViewP);
|
||||||
|
|
||||||
@@ -104,7 +105,6 @@ protected:
|
|||||||
// for section filtering
|
// for section filtering
|
||||||
public:
|
public:
|
||||||
virtual int OpenFilter(u_short pidP, u_char tidP, u_char maskP);
|
virtual int OpenFilter(u_short pidP, u_char tidP, u_char maskP);
|
||||||
virtual int ReadFilter(int handleP, void *bufferP, size_t lengthP);
|
|
||||||
virtual void CloseFilter(int handleP);
|
virtual void CloseFilter(int handleP);
|
||||||
|
|
||||||
// for transponder lock
|
// for transponder lock
|
||||||
@@ -114,6 +114,11 @@ public:
|
|||||||
// for common interface
|
// for common interface
|
||||||
public:
|
public:
|
||||||
virtual bool HasInternalCam(void);
|
virtual bool HasInternalCam(void);
|
||||||
|
|
||||||
|
// for internal device interface
|
||||||
|
public:
|
||||||
|
virtual void WriteData(u_char *bufferP, int lengthP);
|
||||||
|
virtual unsigned int CheckData(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __IPTV_DEVICE_H
|
#endif // __IPTV_DEVICE_H
|
||||||
|
|||||||
23
deviceif.h
Normal file
23
deviceif.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* deviceif.h: IPTV plugin for the Video Disk Recorder
|
||||||
|
*
|
||||||
|
* See the README file for copyright information and how to reach the author.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IPTV_DEVICEIF_H
|
||||||
|
#define __IPTV_DEVICEIF_H
|
||||||
|
|
||||||
|
class cIptvDeviceIf {
|
||||||
|
public:
|
||||||
|
cIptvDeviceIf() {}
|
||||||
|
virtual ~cIptvDeviceIf() {}
|
||||||
|
virtual void WriteData(u_char *bufferP, int lengthP) = 0;
|
||||||
|
virtual unsigned int CheckData(void) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
cIptvDeviceIf(const cIptvDeviceIf&);
|
||||||
|
cIptvDeviceIf& operator=(const cIptvDeviceIf&);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __IPTV_DEVICEIF_H
|
||||||
10
iptv.c
10
iptv.c
@@ -13,15 +13,15 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "iptvservice.h"
|
#include "iptvservice.h"
|
||||||
|
|
||||||
#if defined(APIVERSNUM) && APIVERSNUM < 10738
|
#if defined(APIVERSNUM) && APIVERSNUM < 20000
|
||||||
#error "VDR-1.7.38 API version or greater is required!"
|
#error "VDR-2.0.0 API version or greater is required!"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GITVERSION
|
#ifndef GITVERSION
|
||||||
#define GITVERSION ""
|
#define GITVERSION ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char VERSION[] = "1.2.0" GITVERSION;
|
const char VERSION[] = "2.0.3" GITVERSION;
|
||||||
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
||||||
|
|
||||||
class cPluginIptv : public cPlugin {
|
class cPluginIptv : public cPlugin {
|
||||||
@@ -99,7 +99,8 @@ bool cPluginIptv::Initialize(void)
|
|||||||
{
|
{
|
||||||
debug("cPluginIptv::%s()", __FUNCTION__);
|
debug("cPluginIptv::%s()", __FUNCTION__);
|
||||||
// Initialize any background activities the plugin shall perform.
|
// Initialize any background activities the plugin shall perform.
|
||||||
IptvConfig.SetConfigDirectory(cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
|
IptvConfig.SetConfigDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N));
|
||||||
|
IptvConfig.SetResourceDirectory(cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
|
||||||
return cIptvDevice::Initialize(deviceCountM);
|
return cIptvDevice::Initialize(deviceCountM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ void cPluginIptv::Stop(void)
|
|||||||
{
|
{
|
||||||
debug("cPluginIptv::%s()", __FUNCTION__);
|
debug("cPluginIptv::%s()", __FUNCTION__);
|
||||||
// Stop any background activities the plugin is performing.
|
// Stop any background activities the plugin is performing.
|
||||||
|
cIptvDevice::Shutdown();
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ void cPidScanner::SetChannel(const tChannelID &channelIdP)
|
|||||||
aPidM = 0xFFFF;
|
aPidM = 0xFFFF;
|
||||||
numApidsM = 0;
|
numApidsM = 0;
|
||||||
processM = true;
|
processM = true;
|
||||||
timeoutM.Set(PIDSCANNER_TIMEOUT_IN_MS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPidScanner::Process(const uint8_t* bufP)
|
void cPidScanner::Process(const uint8_t* bufP)
|
||||||
@@ -56,7 +55,7 @@ void cPidScanner::Process(const uint8_t* bufP)
|
|||||||
|
|
||||||
// Verify TS packet
|
// Verify TS packet
|
||||||
if (bufP[0] != 0x47) {
|
if (bufP[0] != 0x47) {
|
||||||
error("Not TS packet: 0x%X\n", bufP[0]);
|
error("Not TS packet: 0x%02X", bufP[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
|
|
||||||
class cPidScanner {
|
class cPidScanner {
|
||||||
private:
|
private:
|
||||||
|
enum {
|
||||||
|
PIDSCANNER_APID_COUNT = 5, /* minimum count of audio pid samples for pid detection */
|
||||||
|
PIDSCANNER_VPID_COUNT = 5, /* minimum count of video pid samples for pid detection */
|
||||||
|
PIDSCANNER_PID_DELTA_COUNT = 100, /* minimum count of pid samples for audio/video only pid detection */
|
||||||
|
PIDSCANNER_TIMEOUT_IN_MS = 15000 /* 15s timeout for detection */
|
||||||
|
};
|
||||||
cTimeMs timeoutM;
|
cTimeMs timeoutM;
|
||||||
tChannelID channelIdM;
|
tChannelID channelIdM;
|
||||||
bool processM;
|
bool processM;
|
||||||
@@ -26,6 +32,8 @@ public:
|
|||||||
~cPidScanner();
|
~cPidScanner();
|
||||||
void SetChannel(const tChannelID &channelIdP);
|
void SetChannel(const tChannelID &channelIdP);
|
||||||
void Process(const uint8_t* bufP);
|
void Process(const uint8_t* bufP);
|
||||||
|
void Open() { debug("cPidScanner::%s()", __FUNCTION__); timeoutM.Set(PIDSCANNER_TIMEOUT_IN_MS); }
|
||||||
|
void Close() { debug("cPidScanner::%s()", __FUNCTION__); timeoutM.Set(0); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __PIDSCANNER_H
|
#endif // __PIDSCANNER_H
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# VDR plugin language source file.
|
# VDR plugin language source file.
|
||||||
# Copyright (C) 2007 Rolf Ahrenberg & Antti Seppala
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
# This file is distributed under the same license as the iptv package.
|
# This file is distributed under the same license as the iptv package.
|
||||||
# Tobias <vdr@e-tobi.net>, 2007.
|
# Tobias <vdr@e-tobi.net>, 2007.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
"Project-Id-Version: vdr-iptv 2.0.3\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
|
||||||
"Last-Translator: Tobias Grimm <tg@e-tobi.net>\n"
|
"Last-Translator: Tobias Grimm <tg@e-tobi.net>\n"
|
||||||
"Language-Team: German <vdr@linuxtv.org>\n"
|
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# VDR plugin language source file.
|
# VDR plugin language source file.
|
||||||
# Copyright (C) 2007 Rolf Ahrenberg & Antti Seppala
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
# This file is distributed under the same license as the iptv package.
|
# This file is distributed under the same license as the iptv package.
|
||||||
# Rolf Ahrenberg
|
# Rolf Ahrenberg
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
"Project-Id-Version: vdr-iptv 2.0.3\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
|
||||||
"Last-Translator: Rolf Ahrenberg\n"
|
"Last-Translator: Rolf Ahrenberg\n"
|
||||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||||
"Language: fi\n"
|
"Language: fi\n"
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# French translations for iptv package.
|
# French translations for iptv package.
|
||||||
# Copyright (C) 2007 Rolf Ahrenberg & Antti Seppala
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
# This file is distributed under the same license as the iptv package.
|
# This file is distributed under the same license as the iptv package.
|
||||||
# Bruno ROUSSEL <bruno.roussel@free.fr>, 2007.
|
# Bruno ROUSSEL <bruno.roussel@free.fr>, 2007.
|
||||||
# NIVAL Michaël <mnival@club-internet.fr>, 2008.
|
# NIVAL Michaël <mnival@club-internet.fr>, 2008.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
"Project-Id-Version: vdr-iptv 2.0.3\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
|
||||||
"Last-Translator: NIVAL Michaël <mnival@club-internet.fr>\n"
|
"Last-Translator: NIVAL Michaël <mnival@club-internet.fr>\n"
|
||||||
"Language-Team: French <vdr@linuxtv.org>\n"
|
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# VDR plugin language source file.
|
# VDR plugin language source file.
|
||||||
# Copyright (C) 2007 Rolf Ahrenberg & Antti Seppala
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
# This file is distributed under the same license as the iptv package.
|
# This file is distributed under the same license as the iptv package.
|
||||||
# Diego Pierotto <vdr-italian@tiscali.it>, 2008.
|
# Diego Pierotto <vdr-italian@tiscali.it>, 2008.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
"Project-Id-Version: vdr-iptv 2.0.3\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
|
||||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||||
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
||||||
"Language: it\n"
|
"Language: it\n"
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# VDR plugin language source file.
|
# VDR plugin language source file.
|
||||||
# Copyright (C) 2007 Rolf Ahrenberg & Antti Seppala
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
# This file is distributed under the same license as the iptv package.
|
# This file is distributed under the same license as the iptv package.
|
||||||
# Carel, 2010.
|
# Carel, 2010.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
"Project-Id-Version: vdr-iptv 2.0.3\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
|
||||||
"Last-Translator: Carel\n"
|
"Last-Translator: Carel\n"
|
||||||
"Language-Team: Dutch <vdr@linuxtv.org>\n"
|
"Language-Team: Dutch <vdr@linuxtv.org>\n"
|
||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# VDR plugin language source file.
|
# VDR plugin language source file.
|
||||||
# Copyright (C) 2007 Rolf Ahrenberg & Antti Seppala
|
# Copyright (C) 2007-2014 Rolf Ahrenberg & Antti Seppala
|
||||||
# This file is distributed under the same license as the iptv package.
|
# This file is distributed under the same license as the iptv package.
|
||||||
# Alexander Gross <Bikalexander@gmail.com>, 2008.
|
# Alexander Gross <Bikalexander@gmail.com>, 2008.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: vdr-iptv 1.2.0\n"
|
"Project-Id-Version: vdr-iptv 2.0.3\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\n"
|
"Report-Msgid-Bugs-To: <see README>\n"
|
||||||
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
|
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
|
||||||
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
|
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
|
||||||
"Last-Translator: Alexander Gross <Bikalexander@gmail.com>\n"
|
"Last-Translator: Alexander Gross <Bikalexander@gmail.com>\n"
|
||||||
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
|
|||||||
203
protocolcurl.c
203
protocolcurl.c
@@ -9,11 +9,19 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "protocolcurl.h"
|
#include "protocolcurl.h"
|
||||||
|
|
||||||
|
#ifdef CURLOPT_RTSPHEADER
|
||||||
|
#define USE_RTSP
|
||||||
|
#endif
|
||||||
|
|
||||||
#define iptv_curl_easy_setopt(X, Y, Z) \
|
#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) failed: %s (%d)", #Y, #Z, curl_easy_strerror(res), res); \
|
||||||
|
}
|
||||||
|
|
||||||
#define iptv_curl_easy_perform(X) \
|
#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() failed: %s (%d)", curl_easy_strerror(res), res); \
|
||||||
|
}
|
||||||
|
|
||||||
cIptvProtocolCurl::cIptvProtocolCurl()
|
cIptvProtocolCurl::cIptvProtocolCurl()
|
||||||
: streamUrlM(""),
|
: streamUrlM(""),
|
||||||
@@ -22,15 +30,18 @@ cIptvProtocolCurl::cIptvProtocolCurl()
|
|||||||
handleM(NULL),
|
handleM(NULL),
|
||||||
multiM(NULL),
|
multiM(NULL),
|
||||||
headerListM(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(),
|
rtspControlM(),
|
||||||
modeM(eModeUnknown),
|
modeM(eModeUnknown),
|
||||||
connectedM(false),
|
connectedM(false),
|
||||||
pausedM(false)
|
pausedM(false)
|
||||||
{
|
{
|
||||||
debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
||||||
if (ringBufferM)
|
if (ringBufferM) {
|
||||||
ringBufferM->SetTimeouts(100, 0);
|
ringBufferM->SetTimeouts(100, 0);
|
||||||
|
ringBufferM->SetIoThrottle();
|
||||||
|
}
|
||||||
Connect();
|
Connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,30 +71,30 @@ size_t cIptvProtocolCurl::WriteRtspCallback(void *ptrP, size_t sizeP, size_t nme
|
|||||||
size_t len = sizeP * nmembP;
|
size_t len = sizeP * nmembP;
|
||||||
unsigned char *p = (unsigned char *)ptrP;
|
unsigned char *p = (unsigned char *)ptrP;
|
||||||
//debug("cIptvProtocolCurl::%s(%zu)", __FUNCTION__, len);
|
//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)) {
|
if (obj && (p[0] == 0x24) && (p[1] == 0)) {
|
||||||
int length = (p[2] << 8) | p[3];
|
int length = (p[2] << 8) | p[3];
|
||||||
if (length > 3) {
|
if (length > 3) {
|
||||||
// skip interleave header
|
// Skip interleave header
|
||||||
p += 4;
|
p += 4;
|
||||||
// http://tools.ietf.org/html/rfc3550
|
// http://tools.ietf.org/html/rfc3550
|
||||||
// http://tools.ietf.org/html/rfc2250
|
// http://tools.ietf.org/html/rfc2250
|
||||||
// version
|
// Version
|
||||||
unsigned int v = (p[0] >> 6) & 0x03;
|
unsigned int v = (p[0] >> 6) & 0x03;
|
||||||
// extension bit
|
// Extension bit
|
||||||
unsigned int x = (p[0] >> 4) & 0x01;
|
unsigned int x = (p[0] >> 4) & 0x01;
|
||||||
// cscr count
|
// CSCR count
|
||||||
unsigned int cc = p[0] & 0x0F;
|
unsigned int cc = p[0] & 0x0F;
|
||||||
// payload type: MPEG2 TS = 33
|
// Payload type: MPEG2 TS = 33
|
||||||
//unsigned int pt = p[1] & 0x7F;
|
//unsigned int pt = p[1] & 0x7F;
|
||||||
// header lenght
|
// Header lenght
|
||||||
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
||||||
// check if extension
|
// Check if extension
|
||||||
if (x) {
|
if (x) {
|
||||||
// extension header length
|
// Extension header length
|
||||||
unsigned int ehl = (((p[headerlen + 2] & 0xFF) << 8) |(p[headerlen + 3] & 0xFF));
|
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);
|
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
||||||
@@ -116,7 +127,7 @@ size_t cIptvProtocolCurl::DescribeCallback(void *ptrP, size_t sizeP, size_t nmem
|
|||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
r = strtok(NULL, "\r\n");
|
r = strtok(NULL, "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isempty(*control) && obj)
|
if (!isempty(*control) && obj)
|
||||||
obj->SetRtspControl(*control);
|
obj->SetRtspControl(*control);
|
||||||
@@ -155,9 +166,10 @@ bool cIptvProtocolCurl::PutData(unsigned char *dataP, int lenP)
|
|||||||
if (pausedM)
|
if (pausedM)
|
||||||
return false;
|
return false;
|
||||||
if (ringBufferM && (lenP >= 0)) {
|
if (ringBufferM && (lenP >= 0)) {
|
||||||
// should be pause the transfer?
|
// Should we pause the transfer ?
|
||||||
if (ringBufferM->Free() < (2 * CURL_MAX_WRITE_SIZE)) {
|
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;
|
pausedM = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -184,12 +196,12 @@ void cIptvProtocolCurl::ClearData()
|
|||||||
ringBufferM->Clear();
|
ringBufferM->Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *cIptvProtocolCurl::GetData(unsigned int *lenP)
|
unsigned char *cIptvProtocolCurl::GetData(int &lenP)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutexM);
|
cMutexLock MutexLock(&mutexM);
|
||||||
//debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
//debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
||||||
unsigned char *p = NULL;
|
unsigned char *p = NULL;
|
||||||
*lenP = 0;
|
lenP = 0;
|
||||||
if (ringBufferM) {
|
if (ringBufferM) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
p = ringBufferM->Get(count);
|
p = ringBufferM->Get(count);
|
||||||
@@ -202,15 +214,15 @@ unsigned char *cIptvProtocolCurl::GetData(unsigned int *lenP)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
error("IPTV skipped %d bytes to sync on TS packet\n", count);
|
error("IPTV skipped %d bytes to sync on TS packet", count);
|
||||||
ringBufferM->Del(count);
|
ringBufferM->Del(count);
|
||||||
*lenP = 0;
|
lenP = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
count -= (count % TS_SIZE);
|
count -= (count % TS_SIZE);
|
||||||
*lenP = count;
|
lenP = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
@@ -223,7 +235,7 @@ bool cIptvProtocolCurl::Connect()
|
|||||||
if (connectedM)
|
if (connectedM)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// initialize the curl session
|
// Initialize the curl session
|
||||||
if (!handleM)
|
if (!handleM)
|
||||||
handleM = curl_easy_init();
|
handleM = curl_easy_init();
|
||||||
if (!multiM)
|
if (!multiM)
|
||||||
@@ -234,95 +246,97 @@ bool cIptvProtocolCurl::Connect()
|
|||||||
cString netrc = cString::sprintf("%s/netrc", IptvConfig.GetConfigDirectory());
|
cString netrc = cString::sprintf("%s/netrc", IptvConfig.GetConfigDirectory());
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// verbose output
|
// Verbose output
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_VERBOSE, 1L);
|
iptv_curl_easy_setopt(handleM, CURLOPT_VERBOSE, 1L);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// set callbacks
|
// Set callbacks
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::WriteCallback);
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, cIptvProtocolCurl::WriteCallback);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_HEADERFUNCTION, cIptvProtocolCurl::HeaderCallback);
|
iptv_curl_easy_setopt(handleM, CURLOPT_HEADERFUNCTION, cIptvProtocolCurl::HeaderCallback);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEHEADER, this);
|
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_NOPROGRESS, 1L);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_NOSIGNAL, 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, (long)CURL_NETRC_OPTIONAL);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC_FILE, *netrc);
|
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC_FILE, *netrc);
|
||||||
|
|
||||||
// set timeout
|
// Set timeouts
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_TIMEOUT, (long)eConnectTimeoutS);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_CONNECTTIMEOUT, (long)eConnectTimeoutS);
|
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));
|
iptv_curl_easy_setopt(handleM, CURLOPT_USERAGENT, *cString::sprintf("vdr-%s/%s", PLUGIN_NAME_I18N, VERSION));
|
||||||
|
|
||||||
// set url
|
// Set URL
|
||||||
//char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL);
|
char *p = curl_easy_unescape(handleM, *streamUrlM, 0, NULL);
|
||||||
//streamUrlM = p;
|
streamUrlM = p;
|
||||||
//curl_free(p);
|
curl_free(p);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_URL, *streamUrlM);
|
iptv_curl_easy_setopt(handleM, CURLOPT_URL, *streamUrlM);
|
||||||
|
|
||||||
// protocol specific initializations
|
// Protocol specific initializations
|
||||||
switch (modeM) {
|
switch (modeM) {
|
||||||
|
#ifdef USE_RTSP
|
||||||
case eModeRtsp:
|
case eModeRtsp:
|
||||||
{
|
{
|
||||||
cString uri, control, transport, range;
|
cString uri, control, transport, range;
|
||||||
|
|
||||||
// request server options
|
// Request server options
|
||||||
uri = cString::sprintf("%s", *streamUrlM);
|
uri = cString::sprintf("%s", *streamUrlM);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
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);
|
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_WRITEFUNCTION, cIptvProtocolCurl::DescribeCallback);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, this);
|
||||||
uri = cString::sprintf("%s", *streamUrlM);
|
uri = cString::sprintf("%s", *streamUrlM);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
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_perform(handleM);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, NULL);
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEFUNCTION, NULL);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, NULL);
|
iptv_curl_easy_setopt(handleM, CURLOPT_WRITEDATA, NULL);
|
||||||
|
|
||||||
// setup media stream
|
// Setup media stream
|
||||||
uri = cString::sprintf("%s/%s", *streamUrlM, *rtspControlM);
|
uri = cString::sprintf("%s/%s", *streamUrlM, *rtspControlM);
|
||||||
transport = "RTP/AVP/TCP;unicast;interleaved=0-1";
|
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_STREAM_URI, *uri);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_TRANSPORT, *transport);
|
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);
|
iptv_curl_easy_perform(handleM);
|
||||||
|
|
||||||
// start playing
|
// Start playing
|
||||||
uri = cString::sprintf("%s/", *streamUrlM);
|
uri = cString::sprintf("%s/", *streamUrlM);
|
||||||
range = "0.000-";
|
range = "0.000-";
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_RANGE, *range);
|
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);
|
iptv_curl_easy_perform(handleM);
|
||||||
|
|
||||||
// start receiving
|
// Start receiving
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEFUNCTION, cIptvProtocolCurl::WriteRtspCallback);
|
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEFUNCTION, cIptvProtocolCurl::WriteRtspCallback);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_INTERLEAVEDATA, this);
|
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);
|
iptv_curl_easy_perform(handleM);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case eModeHttp:
|
case eModeHttp:
|
||||||
case eModeHttps:
|
case eModeHttps:
|
||||||
{
|
{
|
||||||
// limit download speed (bytes/s)
|
// Limit download speed (bytes/s)
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L);
|
iptv_curl_easy_setopt(handleM, CURLOPT_MAX_RECV_SPEED_LARGE, eMaxDownloadSpeedMBits * 131072L);
|
||||||
|
|
||||||
// follow location
|
// Follow location
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_FOLLOWLOCATION, 1L);
|
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);
|
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: no-store, no-cache, must-revalidate");
|
||||||
headerListM = curl_slist_append(headerListM, "Cache-Control: post-check=0, pre-check=0");
|
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, "Pragma: no-cache");
|
||||||
@@ -332,12 +346,16 @@ bool cIptvProtocolCurl::Connect()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case eModeFile:
|
case eModeFile:
|
||||||
|
// Set timeout
|
||||||
|
iptv_curl_easy_setopt(handleM, CURLOPT_TIMEOUT_MS, 10L);
|
||||||
|
break;
|
||||||
|
|
||||||
case eModeUnknown:
|
case eModeUnknown:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add handle into multi set
|
// Add handle into multi set
|
||||||
curl_multi_add_handle(multiM, handleM);
|
curl_multi_add_handle(multiM, handleM);
|
||||||
|
|
||||||
connectedM = true;
|
connectedM = true;
|
||||||
@@ -351,13 +369,18 @@ bool cIptvProtocolCurl::Disconnect()
|
|||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutexM);
|
cMutexLock MutexLock(&mutexM);
|
||||||
debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
||||||
if (handleM) {
|
if (!connectedM)
|
||||||
// mode specific tricks
|
return true;
|
||||||
|
|
||||||
|
// Terminate curl session
|
||||||
|
if (handleM && multiM) {
|
||||||
|
// Mode specific tricks
|
||||||
switch (modeM) {
|
switch (modeM) {
|
||||||
|
#ifdef USE_RTSP
|
||||||
case eModeRtsp:
|
case eModeRtsp:
|
||||||
{
|
{
|
||||||
CURLcode res = CURLE_OK;
|
CURLcode res = CURLE_OK;
|
||||||
// teardown rtsp session
|
// Teardown rtsp session
|
||||||
cString uri = cString::sprintf("%s/", *streamUrlM);
|
cString uri = cString::sprintf("%s/", *streamUrlM);
|
||||||
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_STREAM_URI, *uri);
|
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_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
|
||||||
@@ -365,7 +388,7 @@ bool cIptvProtocolCurl::Disconnect()
|
|||||||
rtspControlM = "";
|
rtspControlM = "";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case eModeHttp:
|
case eModeHttp:
|
||||||
case eModeHttps:
|
case eModeHttps:
|
||||||
case eModeFile:
|
case eModeFile:
|
||||||
@@ -374,16 +397,16 @@ bool cIptvProtocolCurl::Disconnect()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup curl stuff
|
// Cleanup curl stuff
|
||||||
if (headerListM) {
|
if (headerListM) {
|
||||||
curl_slist_free_all(headerListM);
|
curl_slist_free_all(headerListM);
|
||||||
headerListM = NULL;
|
headerListM = NULL;
|
||||||
}
|
}
|
||||||
curl_multi_remove_handle(multiM, handleM);
|
curl_multi_remove_handle(multiM, handleM);
|
||||||
curl_multi_cleanup(multiM);
|
|
||||||
multiM = NULL;
|
|
||||||
curl_easy_cleanup(handleM);
|
curl_easy_cleanup(handleM);
|
||||||
handleM = NULL;
|
handleM = NULL;
|
||||||
|
curl_multi_cleanup(multiM);
|
||||||
|
multiM = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearData();
|
ClearData();
|
||||||
@@ -409,21 +432,21 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
|
|||||||
//debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
//debug("cIptvProtocolCurl::%s()", __FUNCTION__);
|
||||||
int len = 0;
|
int len = 0;
|
||||||
if (ringBufferM) {
|
if (ringBufferM) {
|
||||||
// fill up the buffer
|
// Fill up the buffer
|
||||||
if (handleM && multiM) {
|
if (handleM && multiM) {
|
||||||
switch (modeM) {
|
switch (modeM) {
|
||||||
|
#ifdef USE_RTSP
|
||||||
case eModeRtsp:
|
case eModeRtsp:
|
||||||
{
|
{
|
||||||
//CURLcode res = CURLE_OK;
|
cMutexLock MutexLock(&mutexM);
|
||||||
//iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE);
|
CURLcode res = CURLE_OK;
|
||||||
//iptv_curl_easy_perform(handleM);
|
iptv_curl_easy_setopt(handleM, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_RECEIVE);
|
||||||
// @todo - how to detect eof?
|
iptv_curl_easy_perform(handleM);
|
||||||
|
// @todo - How to detect eof?
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case eModeFile:
|
case eModeFile:
|
||||||
break;
|
|
||||||
|
|
||||||
case eModeHttp:
|
case eModeHttp:
|
||||||
case eModeHttps:
|
case eModeHttps:
|
||||||
{
|
{
|
||||||
@@ -431,24 +454,29 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
|
|||||||
int running_handles;
|
int running_handles;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
res = curl_multi_perform(multiM, &running_handles);
|
res = curl_multi_perform(multiM, &running_handles);
|
||||||
} while (res == CURLM_CALL_MULTI_PERFORM);
|
} while (res == CURLM_CALL_MULTI_PERFORM);
|
||||||
|
|
||||||
// shall be continue filling up the buffer?
|
// Use 20% threshold before continuing to filling up the buffer.
|
||||||
mutexM.Lock();
|
mutexM.Lock();
|
||||||
if (pausedM && (ringBufferM->Free() > ringBufferM->Available())) {
|
if (pausedM && (ringBufferM->Available() < (MEGABYTE(IptvConfig.GetTsBufferSize()) / 5))) {
|
||||||
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;
|
pausedM = false;
|
||||||
curl_easy_pause(handleM, CURLPAUSE_CONT);
|
curl_easy_pause(handleM, CURLPAUSE_CONT);
|
||||||
}
|
}
|
||||||
mutexM.Unlock();
|
mutexM.Unlock();
|
||||||
|
|
||||||
// check end of file
|
// Check if end of file
|
||||||
if (running_handles == 0) {
|
if (running_handles == 0) {
|
||||||
int msgcount;
|
int msgcount;
|
||||||
|
mutexM.Lock();
|
||||||
CURLMsg *msg = curl_multi_info_read(multiM, &msgcount);
|
CURLMsg *msg = curl_multi_info_read(multiM, &msgcount);
|
||||||
|
mutexM.Unlock();
|
||||||
if (msg && (msg->msg == CURLMSG_DONE)) {
|
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();
|
Disconnect();
|
||||||
Connect();
|
Connect();
|
||||||
}
|
}
|
||||||
@@ -456,17 +484,18 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case eModeUnknown:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... and try to empty it
|
// ... and try to empty it
|
||||||
unsigned char *p = GetData(&bufferLenP);
|
unsigned char *p = GetData(len);
|
||||||
if (p && (bufferLenP > 0)) {
|
if (p && (len > 0)) {
|
||||||
memcpy(bufferAddrP, p, bufferLenP);
|
len = min(len, (int)bufferLenP);
|
||||||
DelData(bufferLenP);
|
memcpy(bufferAddrP, p, len);
|
||||||
len = bufferLenP;
|
DelData(len);
|
||||||
//debug("cIptvProtocolCurl::%s(): get %d bytes", __FUNCTION__, len);
|
//debug("cIptvProtocolCurl::%s(): get %d bytes", __FUNCTION__, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,22 +509,20 @@ bool cIptvProtocolCurl::Set(const char* locationP, const int parameterP, const i
|
|||||||
if (!isempty(locationP)) {
|
if (!isempty(locationP)) {
|
||||||
// Disconnect
|
// Disconnect
|
||||||
Disconnect();
|
Disconnect();
|
||||||
// Update stream URL: colons (%3A) and pipes (%7C) shall be decoded
|
// Update stream URL
|
||||||
char *s = strdup(locationP);
|
streamUrlM = locationP;
|
||||||
strreplace(s, "%3A", ":");
|
cString protocol = ChangeCase(streamUrlM, false).Truncate(5);
|
||||||
strreplace(s, "%7C", "|");
|
if (startswith(*protocol, "rtsp"))
|
||||||
streamUrlM = s;
|
|
||||||
free(s);
|
|
||||||
if (startswith(*streamUrlM, "rtsp") || startswith(*streamUrlM, "RTSP"))
|
|
||||||
modeM = eModeRtsp;
|
modeM = eModeRtsp;
|
||||||
else if (startswith(*streamUrlM, "https") || startswith(*streamUrlM, "HTTPS"))
|
else if (startswith(*protocol, "https"))
|
||||||
modeM = eModeHttp;
|
|
||||||
else if (startswith(*streamUrlM, "http") || startswith(*streamUrlM, "HTTP"))
|
|
||||||
modeM = eModeHttps;
|
modeM = eModeHttps;
|
||||||
else if (startswith(*streamUrlM, "file") || startswith(*streamUrlM, "FILE"))
|
else if (startswith(*protocol, "http"))
|
||||||
|
modeM = eModeHttp;
|
||||||
|
else if (startswith(*protocol, "file"))
|
||||||
modeM = eModeFile;
|
modeM = eModeFile;
|
||||||
else
|
else
|
||||||
modeM = eModeUnknown;
|
modeM = eModeUnknown;
|
||||||
|
debug("cIptvProtocolCurl::%s(): %s (%d)", __FUNCTION__, *protocol, modeM);
|
||||||
// Update stream parameter
|
// Update stream parameter
|
||||||
streamParamM = parameterP;
|
streamParamM = parameterP;
|
||||||
// Reconnect
|
// Reconnect
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ private:
|
|||||||
};
|
};
|
||||||
enum {
|
enum {
|
||||||
eConnectTimeoutS = 5, // in seconds
|
eConnectTimeoutS = 5, // in seconds
|
||||||
eSelectTimeoutMs = 10, // in milliseconds
|
|
||||||
eMaxDownloadSpeedMBits = 20 // in megabits per second
|
eMaxDownloadSpeedMBits = 20 // in megabits per second
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ private:
|
|||||||
bool PutData(unsigned char *dataP, int lenP);
|
bool PutData(unsigned char *dataP, int lenP);
|
||||||
void DelData(int lenP);
|
void DelData(int lenP);
|
||||||
void ClearData(void);
|
void ClearData(void);
|
||||||
unsigned char *GetData(unsigned int *lenP);
|
unsigned char *GetData(int &lenP);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cIptvProtocolCurl();
|
cIptvProtocolCurl();
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ bool cIptvProtocolExt::Set(const char* locationP, const int parameterP, const in
|
|||||||
if (!isempty(locationP)) {
|
if (!isempty(locationP)) {
|
||||||
struct stat stbuf;
|
struct stat stbuf;
|
||||||
// Update script file and parameter
|
// Update script file and parameter
|
||||||
scriptFileM = cString::sprintf("%s/%s", IptvConfig.GetConfigDirectory(), locationP);
|
scriptFileM = cString::sprintf("%s/%s", IptvConfig.GetResourceDirectory(), locationP);
|
||||||
if ((stat(*scriptFileM, &stbuf) != 0) || (strstr(*scriptFileM, "..") != 0)) {
|
if ((stat(*scriptFileM, &stbuf) != 0) || (strstr(*scriptFileM, "..") != 0)) {
|
||||||
error("Non-existent or relative path script '%s'", *scriptFileM);
|
error("Non-existent or relative path script '%s'", *scriptFileM);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ int cIptvProtocolFile::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
|
|||||||
{
|
{
|
||||||
//debug("cIptvProtocolFile::%s()", __FUNCTION__);
|
//debug("cIptvProtocolFile::%s()", __FUNCTION__);
|
||||||
// Check errors
|
// Check errors
|
||||||
if (ferror(fileStreamM)) {
|
if (!fileStreamM || ferror(fileStreamM)) {
|
||||||
debug("cIptvProtocolFile::%s(): stream error", __FUNCTION__);
|
debug("cIptvProtocolFile::%s(): stream error", __FUNCTION__);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,9 @@ bool cIptvProtocolHttp::Connect(void)
|
|||||||
"Connection: Close\r\n"
|
"Connection: Close\r\n"
|
||||||
"\r\n", streamPathM, streamAddrM,
|
"\r\n", streamPathM, streamAddrM,
|
||||||
PLUGIN_NAME_I18N, VERSION);
|
PLUGIN_NAME_I18N, VERSION);
|
||||||
debug("cIptvProtocolHttp::%s(): requesting: %s", __FUNCTION__, *buffer);
|
unsigned int len = strlen(*buffer);
|
||||||
if (!Write(*buffer, (unsigned int)strlen(*buffer))) {
|
debug("cIptvProtocolHttp::%s(): requesting %d: %s", __FUNCTION__, len, *buffer);
|
||||||
|
if (!Write(*buffer, len)) {
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -112,13 +113,13 @@ bool cIptvProtocolHttp::GetHeaderLine(char* destP, unsigned int destLenP,
|
|||||||
++bufptr;
|
++bufptr;
|
||||||
// Check that buffer won't be exceeded
|
// Check that buffer won't be exceeded
|
||||||
if (recvLenP >= destLenP) {
|
if (recvLenP >= destLenP) {
|
||||||
error("Header wouldn't fit into buffer\n");
|
error("Header wouldn't fit into buffer");
|
||||||
recvLenP = 0;
|
recvLenP = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
error("No HTTP response received in 500ms\n");
|
error("No HTTP response received in 500ms");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,14 +143,14 @@ bool cIptvProtocolHttp::ProcessHeaders(void)
|
|||||||
if (!GetHeaderLine(buf, sizeof(buf), lineLength))
|
if (!GetHeaderLine(buf, sizeof(buf), lineLength))
|
||||||
return false;
|
return false;
|
||||||
if (!responseFound && sscanf(buf, fmt, &version, &response) != 2) {
|
if (!responseFound && sscanf(buf, fmt, &version, &response) != 2) {
|
||||||
error("Expected HTTP header not found\n");
|
error("Expected HTTP header not found");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
responseFound = true;
|
responseFound = true;
|
||||||
// Allow only 'OK' and 'Partial Content'
|
// Allow only 'OK' and 'Partial Content'
|
||||||
if ((response != 200) && (response != 206)) {
|
if ((response != 200) && (response != 206)) {
|
||||||
error("Invalid HTTP response (%d): %s\n", response, buf);
|
error("Invalid HTTP response (%d): %s", response, buf);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
230
sectionfilter.c
230
sectionfilter.c
@@ -17,9 +17,9 @@ cIptvSectionFilter::cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t
|
|||||||
secLenM(0),
|
secLenM(0),
|
||||||
tsFeedpM(0),
|
tsFeedpM(0),
|
||||||
pidM(pidP),
|
pidM(pidP),
|
||||||
devIdM(deviceIndexP)
|
deviceIndexM(deviceIndexP)
|
||||||
{
|
{
|
||||||
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, devIdM, pidM);
|
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
memset(secBufBaseM, 0, sizeof(secBufBaseM));
|
memset(secBufBaseM, 0, sizeof(secBufBaseM));
|
||||||
@@ -46,32 +46,32 @@ cIptvSectionFilter::cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t
|
|||||||
}
|
}
|
||||||
doneqM = doneq ? 1 : 0;
|
doneqM = doneq ? 1 : 0;
|
||||||
|
|
||||||
// Create filtering buffer
|
// Create sockets
|
||||||
ringbufferM = new cRingBufferLinear(KILOBYTE(128), 0, false, *cString::sprintf("IPTV SECTION %d/%d", devIdM, pidM));
|
socketM[0] = socketM[1] = -1;
|
||||||
if (ringbufferM)
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socketM) != 0) {
|
||||||
ringbufferM->SetTimeouts(10, 10);
|
char tmp[64];
|
||||||
else
|
error("Opening section filter sockets failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
|
||||||
error("Failed to allocate buffer for section filter (device=%d pid=%d): ", devIdM, pidM);
|
}
|
||||||
|
else if ((fcntl(socketM[0], F_SETFL, O_NONBLOCK) != 0) || (fcntl(socketM[1], F_SETFL, O_NONBLOCK) != 0)) {
|
||||||
|
char tmp[64];
|
||||||
|
error("Setting section filter socket to non-blocking mode failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cIptvSectionFilter::~cIptvSectionFilter()
|
cIptvSectionFilter::~cIptvSectionFilter()
|
||||||
{
|
{
|
||||||
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, devIdM, pidM);
|
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
|
||||||
DELETE_POINTER(ringbufferM);
|
int tmp = socketM[1];
|
||||||
|
socketM[1] = -1;
|
||||||
|
if (tmp >= 0)
|
||||||
|
close(tmp);
|
||||||
|
tmp = socketM[0];
|
||||||
|
socketM[0] = -1;
|
||||||
|
if (tmp >= 0)
|
||||||
|
close(tmp);
|
||||||
secBufM = NULL;
|
secBufM = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cIptvSectionFilter::Read(void *Data, size_t Length)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
uchar *p = ringbufferM->Get(count);
|
|
||||||
if (p && count > 0) {
|
|
||||||
memcpy(Data, p, count);
|
|
||||||
ringbufferM->Del(count);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *dataP)
|
inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *dataP)
|
||||||
{
|
{
|
||||||
return (uint16_t)(3 + ((dataP[1] & 0x0f) << 8) + dataP[2]);
|
return (uint16_t)(3 + ((dataP[1] & 0x0f) << 8) + dataP[2]);
|
||||||
@@ -99,10 +99,10 @@ int cIptvSectionFilter::Filter(void)
|
|||||||
if (doneqM && !neq)
|
if (doneqM && !neq)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (ringbufferM) {
|
// There is no data in the read socket, more can be written
|
||||||
int len = ringbufferM->Put(secBufM, secLenM);
|
if ((socketM[0] >= 0) && (socketM[1] >= 0) /*&& !select_single_desc(socketM[0], 0, false)*/) {
|
||||||
if (len != secLenM)
|
ssize_t len = write(socketM[1], secBufM, secLenM);
|
||||||
ringbufferM->ReportOverflow(secLenM - len);
|
ERROR_IF(len < 0, "write()");
|
||||||
// Update statistics
|
// Update statistics
|
||||||
AddSectionStatistic(len, 1);
|
AddSectionStatistic(len, 1);
|
||||||
}
|
}
|
||||||
@@ -210,3 +210,183 @@ void cIptvSectionFilter::Process(const uint8_t* dataP)
|
|||||||
CopyDump(&dataP[p], count);
|
CopyDump(&dataP[p], count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cIptvSectionFilterHandler::cIptvSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP)
|
||||||
|
: cThread("IPTV section handler", true),
|
||||||
|
mutexM(),
|
||||||
|
deviceIndexM(deviceIndexP),
|
||||||
|
processedM(false),
|
||||||
|
ringBufferM(new cRingBufferLinear(bufferLenP, TS_SIZE, false, *cString::sprintf("IPTV SECTION HANDLER %d", deviceIndexP)))
|
||||||
|
{
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
|
||||||
|
// Initialize filter pointers
|
||||||
|
memset(filtersM, 0, sizeof(filtersM));
|
||||||
|
|
||||||
|
// Create input buffer
|
||||||
|
if (ringBufferM) {
|
||||||
|
ringBufferM->SetTimeouts(100, 100);
|
||||||
|
ringBufferM->SetIoThrottle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error("Failed to allocate buffer for section filter handler (device=%d): ", deviceIndexM);
|
||||||
|
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
cIptvSectionFilterHandler::~cIptvSectionFilterHandler()
|
||||||
|
{
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
DELETE_POINTER(ringBufferM);
|
||||||
|
|
||||||
|
// Destroy all filters
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
for (int i = 0; i < eMaxSecFilterCount; ++i)
|
||||||
|
Delete(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvSectionFilterHandler::Stop(void)
|
||||||
|
{
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d): entering", __FUNCTION__, deviceIndexM);
|
||||||
|
// Stop thread
|
||||||
|
if (Running())
|
||||||
|
Cancel(3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cIptvSectionFilterHandler::Action(void)
|
||||||
|
{
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d): entering", __FUNCTION__, deviceIndexM);
|
||||||
|
// Do the thread loop
|
||||||
|
while (Running()) {
|
||||||
|
// Read one TS packet
|
||||||
|
if (ringBufferM) {
|
||||||
|
int len = 0;
|
||||||
|
if (processedM) {
|
||||||
|
ringBufferM->Del(TS_SIZE);
|
||||||
|
processedM = false;
|
||||||
|
}
|
||||||
|
uchar *p = ringBufferM->Get(len);
|
||||||
|
if (p && (len >= TS_SIZE)) {
|
||||||
|
if (*p != TS_SYNC_BYTE) {
|
||||||
|
for (int i = 1; i < len; ++i) {
|
||||||
|
if (p[i] == TS_SYNC_BYTE) {
|
||||||
|
len = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ringBufferM->Del(len);
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d): Skipped %d bytes to sync on TS packet", __FUNCTION__, deviceIndexM, len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Process TS packet through all filters
|
||||||
|
mutexM.Lock();
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i])
|
||||||
|
filtersM[i]->Process(p);
|
||||||
|
}
|
||||||
|
mutexM.Unlock();
|
||||||
|
processedM = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cCondWait::SleepMs(10); // to avoid busy loop and reduce cpu load
|
||||||
|
}
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d): exiting", __FUNCTION__, deviceIndexM);
|
||||||
|
}
|
||||||
|
|
||||||
|
cString cIptvSectionFilterHandler::GetInformation(void)
|
||||||
|
{
|
||||||
|
//debug("cIptvSectionFilterHandler::%s(%d)", __FUNCTION__, deviceIndexM);
|
||||||
|
// loop through active section filters
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
cString s = "";
|
||||||
|
unsigned int count = 0;
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i]) {
|
||||||
|
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
|
||||||
|
*filtersM[i]->GetSectionStatistic(), filtersM[i]->GetPid(),
|
||||||
|
id_pid(filtersM[i]->GetPid()));
|
||||||
|
if (++count > IPTV_STATS_ACTIVE_FILTERS_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvSectionFilterHandler::Delete(unsigned int indexP)
|
||||||
|
{
|
||||||
|
//debug("cIptvSectionFilterHandler::%s(%d): index=%d", __FUNCTION__, deviceIndexM, indexP);
|
||||||
|
if ((indexP < eMaxSecFilterCount) && filtersM[indexP]) {
|
||||||
|
//debug("cIptvSectionFilterHandler::%s(%d): found %d", __FUNCTION__, deviceIndexM, indexP);
|
||||||
|
cIptvSectionFilter *tmp = filtersM[indexP];
|
||||||
|
filtersM[indexP] = NULL;
|
||||||
|
delete tmp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cIptvSectionFilterHandler::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const
|
||||||
|
{
|
||||||
|
//debug("cIptvSectionFilterHandler::%s(%d): pid=%d tid=%02X mask=%02X", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
|
||||||
|
// loop through section filter table
|
||||||
|
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
|
||||||
|
int index = IptvConfig.GetDisabledFilters(i);
|
||||||
|
// 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)) {
|
||||||
|
//debug("cIptvSectionFilterHandler::%s(%d): found %s", __FUNCTION__, deviceIndexM, section_filter_table[index].description);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cIptvSectionFilterHandler::Open(u_short pidP, u_char tidP, u_char maskP)
|
||||||
|
{
|
||||||
|
// Lock
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
// Blacklist check, refuse certain filters
|
||||||
|
if (IsBlackListed(pidP, tidP, maskP))
|
||||||
|
return -1;
|
||||||
|
// Search the next free filter slot
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (!filtersM[i]) {
|
||||||
|
filtersM[i] = new cIptvSectionFilter(deviceIndexM, pidP, tidP, maskP);
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d): pid=%d tid=%02X mask=%02X handle=%d index=%d", __FUNCTION__, deviceIndexM, pidP, tidP, maskP, filtersM[i]->GetFd(), i);
|
||||||
|
return filtersM[i]->GetFd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No free filter slot found
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cIptvSectionFilterHandler::Close(int handleP)
|
||||||
|
{
|
||||||
|
// Lock
|
||||||
|
cMutexLock MutexLock(&mutexM);
|
||||||
|
// Search the filter for deletion
|
||||||
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
|
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
|
||||||
|
debug("cIptvSectionFilterHandler::%s(%d): pid=%d handle=%d index=%d", __FUNCTION__, deviceIndexM, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i);
|
||||||
|
Delete(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cIptvSectionFilterHandler::Write(uchar *bufferP, int lengthP)
|
||||||
|
{
|
||||||
|
//debug("cIptvSectionFilterHandler::%s(%d): length=%d", __FUNCTION__, deviceIndexM, lengthP);
|
||||||
|
// Fill up the buffer
|
||||||
|
if (ringBufferM) {
|
||||||
|
int len = ringBufferM->Put(bufferP, lengthP);
|
||||||
|
if (len != lengthP)
|
||||||
|
ringBufferM->ReportOverflow(lengthP - len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ private:
|
|||||||
uint16_t tsFeedpM;
|
uint16_t tsFeedpM;
|
||||||
uint16_t pidM;
|
uint16_t pidM;
|
||||||
|
|
||||||
int devIdM;
|
int deviceIndexM;
|
||||||
|
int socketM[2];
|
||||||
|
|
||||||
uint8_t filterValueM[DMX_MAX_FILTER_SIZE];
|
uint8_t filterValueM[DMX_MAX_FILTER_SIZE];
|
||||||
uint8_t filterMaskM[DMX_MAX_FILTER_SIZE];
|
uint8_t filterMaskM[DMX_MAX_FILTER_SIZE];
|
||||||
@@ -44,8 +45,6 @@ private:
|
|||||||
uint8_t maskAndModeM[DMX_MAX_FILTER_SIZE];
|
uint8_t maskAndModeM[DMX_MAX_FILTER_SIZE];
|
||||||
uint8_t maskAndNotModeM[DMX_MAX_FILTER_SIZE];
|
uint8_t maskAndNotModeM[DMX_MAX_FILTER_SIZE];
|
||||||
|
|
||||||
cRingBufferLinear *ringbufferM;
|
|
||||||
|
|
||||||
inline uint16_t GetLength(const uint8_t *dataP);
|
inline uint16_t GetLength(const uint8_t *dataP);
|
||||||
void New(void);
|
void New(void);
|
||||||
int Filter(void);
|
int Filter(void);
|
||||||
@@ -57,8 +56,35 @@ public:
|
|||||||
cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP);
|
cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP);
|
||||||
virtual ~cIptvSectionFilter();
|
virtual ~cIptvSectionFilter();
|
||||||
void Process(const uint8_t* dataP);
|
void Process(const uint8_t* dataP);
|
||||||
int Read(void *bufferP, size_t lengthP);
|
int GetFd(void) { return socketM[0]; }
|
||||||
uint16_t GetPid(void) const { return pidM; }
|
uint16_t GetPid(void) const { return pidM; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class cIptvSectionFilterHandler : public cThread {
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
eMaxSecFilterCount = 32
|
||||||
|
};
|
||||||
|
cMutex mutexM;
|
||||||
|
int deviceIndexM;
|
||||||
|
bool processedM;
|
||||||
|
cRingBufferLinear *ringBufferM;
|
||||||
|
cIptvSectionFilter *filtersM[eMaxSecFilterCount];
|
||||||
|
|
||||||
|
bool Delete(unsigned int indexP);
|
||||||
|
bool IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void Action(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
cIptvSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP);
|
||||||
|
virtual ~cIptvSectionFilterHandler();
|
||||||
|
bool Stop(void);
|
||||||
|
cString GetInformation(void);
|
||||||
|
int Open(u_short pidP, u_char tidP, u_char maskP);
|
||||||
|
void Close(int handleP);
|
||||||
|
void Write(u_char *bufferP, int lengthP);
|
||||||
|
};
|
||||||
|
|
||||||
#endif // __IPTV_SECTIONFILTER_H
|
#endif // __IPTV_SECTIONFILTER_H
|
||||||
|
|||||||
1
setup.c
1
setup.c
@@ -124,6 +124,7 @@ cIptvPluginSetup::cIptvPluginSetup()
|
|||||||
disabledFilterIndexesM[i] = IptvConfig.GetDisabledFilters(i);
|
disabledFilterIndexesM[i] = IptvConfig.GetDisabledFilters(i);
|
||||||
disabledFilterNamesM[i] = tr(section_filter_table[i].description);
|
disabledFilterNamesM[i] = tr(section_filter_table[i].description);
|
||||||
}
|
}
|
||||||
|
SetMenuCategory(mcSetupPlugins);
|
||||||
Setup();
|
Setup();
|
||||||
SetHelp(NULL, NULL, NULL, trVDR("Button$Info"));
|
SetHelp(NULL, NULL, NULL, trVDR("Button$Info"));
|
||||||
}
|
}
|
||||||
|
|||||||
27
sidscanner.c
27
sidscanner.c
@@ -14,7 +14,8 @@ cSidScanner::cSidScanner(void)
|
|||||||
: channelIdM(tChannelID::InvalidID),
|
: channelIdM(tChannelID::InvalidID),
|
||||||
sidFoundM(false),
|
sidFoundM(false),
|
||||||
nidFoundM(false),
|
nidFoundM(false),
|
||||||
tidFoundM(false)
|
tidFoundM(false),
|
||||||
|
isActiveM(false)
|
||||||
{
|
{
|
||||||
debug("cSidScanner::%s()", __FUNCTION__);
|
debug("cSidScanner::%s()", __FUNCTION__);
|
||||||
Set(0x00, 0x00); // PAT
|
Set(0x00, 0x00); // PAT
|
||||||
@@ -26,12 +27,6 @@ cSidScanner::~cSidScanner()
|
|||||||
debug("cSidScanner::%s()", __FUNCTION__);
|
debug("cSidScanner::%s()", __FUNCTION__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cSidScanner::SetStatus(bool onP)
|
|
||||||
{
|
|
||||||
debug("cSidScanner::%s(%d)", __FUNCTION__, onP);
|
|
||||||
cFilter::SetStatus(onP);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cSidScanner::SetChannel(const tChannelID &channelIdP)
|
void cSidScanner::SetChannel(const tChannelID &channelIdP)
|
||||||
{
|
{
|
||||||
debug("cSidScanner::%s(%s)", __FUNCTION__, *channelIdP.ToString());
|
debug("cSidScanner::%s(%s)", __FUNCTION__, *channelIdP.ToString());
|
||||||
@@ -46,9 +41,11 @@ void cSidScanner::Process(u_short pidP, u_char tidP, const u_char *dataP, int le
|
|||||||
int newSid = -1, newNid = -1, newTid = -1;
|
int newSid = -1, newNid = -1, newTid = -1;
|
||||||
|
|
||||||
//debug("cSidScanner::%s()", __FUNCTION__);
|
//debug("cSidScanner::%s()", __FUNCTION__);
|
||||||
|
if (!isActiveM)
|
||||||
|
return;
|
||||||
if (channelIdM.Valid()) {
|
if (channelIdM.Valid()) {
|
||||||
if ((pidP == 0x00) && (tidP == 0x00)) {
|
if ((pidP == 0x00) && (tidP == 0x00)) {
|
||||||
debug("cSidScanner::%s(%d, %02X)", __FUNCTION__, pidP, tidP);
|
//debug("cSidScanner::%s(%d, %02X)", __FUNCTION__, pidP, tidP);
|
||||||
SI::PAT pat(dataP, false);
|
SI::PAT pat(dataP, false);
|
||||||
if (!pat.CheckCRCAndParse())
|
if (!pat.CheckCRCAndParse())
|
||||||
return;
|
return;
|
||||||
@@ -74,15 +71,21 @@ void cSidScanner::Process(u_short pidP, u_char tidP, const u_char *dataP, int le
|
|||||||
if (ts.getTransportStreamId() != channelIdM.Tid()) {
|
if (ts.getTransportStreamId() != channelIdM.Tid()) {
|
||||||
debug("cSidScanner::%s(): tsid=%d", __FUNCTION__, ts.getTransportStreamId());
|
debug("cSidScanner::%s(): tsid=%d", __FUNCTION__, ts.getTransportStreamId());
|
||||||
newTid = ts.getTransportStreamId();
|
newTid = ts.getTransportStreamId();
|
||||||
|
tidFoundM = true;
|
||||||
|
}
|
||||||
|
if (ts.getOriginalNetworkId() != channelIdM.Nid()) {
|
||||||
|
debug("cSidScanner::%s(): onid=%d", __FUNCTION__, ts.getOriginalNetworkId());
|
||||||
|
newNid = ts.getOriginalNetworkId();
|
||||||
|
nidFoundM = true;
|
||||||
}
|
}
|
||||||
tidFoundM = true;
|
|
||||||
break; // default to the first one
|
break; // default to the first one
|
||||||
}
|
}
|
||||||
if (nit.getNetworkId() != channelIdM.Nid()) {
|
// fallback for network id if not found already
|
||||||
debug("cSidScanner::%s(): nid=%d\n", __FUNCTION__, ts.getTransportStreamId());
|
if (!nidFoundM && (nit.getNetworkId() != channelIdM.Nid())) {
|
||||||
|
debug("cSidScanner::%s(): nid=%d", __FUNCTION__, nit.getNetworkId());
|
||||||
newNid = nit.getNetworkId();
|
newNid = nit.getNetworkId();
|
||||||
|
nidFoundM = true;
|
||||||
}
|
}
|
||||||
nidFoundM = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((newSid >= 0) || (newNid >= 0) || (newTid >= 0)) {
|
if ((newSid >= 0) || (newNid >= 0) || (newTid >= 0)) {
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ private:
|
|||||||
bool sidFoundM;
|
bool sidFoundM;
|
||||||
bool nidFoundM;
|
bool nidFoundM;
|
||||||
bool tidFoundM;
|
bool tidFoundM;
|
||||||
|
bool isActiveM;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Process(u_short pidP, u_char tidP, const u_char *dataP, int lengthP);
|
virtual void Process(u_short pidP, u_char tidP, const u_char *dataP, int lengthP);
|
||||||
virtual void SetStatus(bool onP);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cSidScanner(void);
|
cSidScanner(void);
|
||||||
~cSidScanner();
|
~cSidScanner();
|
||||||
void SetChannel(const tChannelID &channelIdP);
|
void SetChannel(const tChannelID &channelIdP);
|
||||||
void Open() { SetStatus(true); }
|
void Open() { debug("cSidScanner::%s()", __FUNCTION__); isActiveM = true; }
|
||||||
void Close() { SetStatus(false); }
|
void Close() { debug("cSidScanner::%s()", __FUNCTION__); isActiveM = false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SIDSCANNER_H
|
#endif // __SIDSCANNER_H
|
||||||
|
|||||||
42
socket.c
42
socket.c
@@ -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)",
|
ERROR_IF_FUNC(fcntl(socketDescM, F_SETFL, O_NONBLOCK), "fcntl(O_NONBLOCK)",
|
||||||
CloseSocket(), return false);
|
CloseSocket(), return false);
|
||||||
// Allow multiple sockets to use the same PORT number
|
// 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)",
|
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0,
|
||||||
CloseSocket(), return false);
|
"setsockopt(SO_REUSEADDR)", CloseSocket(), return false);
|
||||||
#ifndef __FreeBSD__
|
#ifndef __FreeBSD__
|
||||||
// Allow packet information to be fetched
|
// Allow packet information to be fetched
|
||||||
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0, "setsockopt(IP_PKTINFO)",
|
ERROR_IF_FUNC(setsockopt(socketDescM, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0,
|
||||||
CloseSocket(), return false);
|
"setsockopt(IP_PKTINFO)", CloseSocket(), return false);
|
||||||
#endif // __FreeBSD__
|
#endif // __FreeBSD__
|
||||||
// Bind socket
|
// Bind socket
|
||||||
memset(&sockAddrM, 0, sizeof(sockAddrM));
|
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_port = htons((uint16_t)(portP & 0xFFFF));
|
||||||
sockAddrM.sin_addr.s_addr = htonl(INADDR_ANY);
|
sockAddrM.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
if (isUdpP)
|
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
|
// Update socket port
|
||||||
socketPortM = portP;
|
socketPortM = portP;
|
||||||
}
|
}
|
||||||
@@ -92,17 +93,18 @@ bool cIptvSocket::CheckAddress(const char *addrP, in_addr_t *inAddrP)
|
|||||||
{
|
{
|
||||||
if (inAddrP) {
|
if (inAddrP) {
|
||||||
// First try only the IP address
|
// First try only the IP address
|
||||||
*inAddrP = htonl(inet_addr(addrP));
|
*inAddrP = inet_addr(addrP);
|
||||||
if (*inAddrP == htonl(INADDR_NONE)) {
|
if (*inAddrP == htonl(INADDR_NONE)) {
|
||||||
debug("cIptvSocket::%s(%s): cannot convert to address", __FUNCTION__, addrP);
|
debug("cIptvSocket::%s(%s): cannot convert to address", __FUNCTION__, addrP);
|
||||||
// It may be a host name, get the name
|
// It may be a host name, get the name
|
||||||
struct hostent *host = gethostbyname(addrP);
|
struct hostent *host = gethostbyname(addrP);
|
||||||
if (!host) {
|
if (!host) {
|
||||||
char tmp[64];
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
*inAddrP = htonl(inet_addr(*host->h_addr_list));
|
*inAddrP = inet_addr(*host->h_addr_list);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -224,7 +226,7 @@ int cIptvUdpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
|
|||||||
//debug("cIptvUdpSocket::%s()", __FUNCTION__);
|
//debug("cIptvUdpSocket::%s()", __FUNCTION__);
|
||||||
// Error out if socket not initialized
|
// Error out if socket not initialized
|
||||||
if (socketDescM <= 0) {
|
if (socketDescM <= 0) {
|
||||||
error("Invalid socket in cIptvUdpSocket::%s()\n", __FUNCTION__);
|
error("Invalid socket in cIptvUdpSocket::%s()", __FUNCTION__);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int len = 0;
|
int len = 0;
|
||||||
@@ -264,22 +266,22 @@ int cIptvUdpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
|
|||||||
else if (len > 3) {
|
else if (len > 3) {
|
||||||
// http://tools.ietf.org/html/rfc3550
|
// http://tools.ietf.org/html/rfc3550
|
||||||
// http://tools.ietf.org/html/rfc2250
|
// http://tools.ietf.org/html/rfc2250
|
||||||
// version
|
// Version
|
||||||
unsigned int v = (bufferAddrP[0] >> 6) & 0x03;
|
unsigned int v = (bufferAddrP[0] >> 6) & 0x03;
|
||||||
// extension bit
|
// Extension bit
|
||||||
unsigned int x = (bufferAddrP[0] >> 4) & 0x01;
|
unsigned int x = (bufferAddrP[0] >> 4) & 0x01;
|
||||||
// cscr count
|
// CSCR count
|
||||||
unsigned int cc = bufferAddrP[0] & 0x0F;
|
unsigned int cc = bufferAddrP[0] & 0x0F;
|
||||||
// payload type: MPEG2 TS = 33
|
// Payload type: MPEG2 TS = 33
|
||||||
//unsigned int pt = bufferAddrP[1] & 0x7F;
|
//unsigned int pt = bufferAddrP[1] & 0x7F;
|
||||||
// header lenght
|
// Header lenght
|
||||||
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
|
||||||
// check if extension
|
// Check if extension
|
||||||
if (x) {
|
if (x) {
|
||||||
// extension header length
|
// Extension header length
|
||||||
unsigned int ehl = (((bufferAddrP[headerlen + 2] & 0xFF) << 8) |
|
unsigned int ehl = (((bufferAddrP[headerlen + 2] & 0xFF) << 8) |
|
||||||
(bufferAddrP[headerlen + 3] & 0xFF));
|
(bufferAddrP[headerlen + 3] & 0xFF));
|
||||||
// update header length
|
// Update header length
|
||||||
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
|
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
// Check that rtp is version 2 and payload contains multiple of TS packet data
|
||||||
@@ -357,7 +359,7 @@ int cIptvTcpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
|
|||||||
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
|
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
|
||||||
// Error out if socket not initialized
|
// Error out if socket not initialized
|
||||||
if (socketDescM <= 0) {
|
if (socketDescM <= 0) {
|
||||||
error("Invalid socket in cIptvTcpSocket::%s()\n", __FUNCTION__);
|
error("Invalid socket in cIptvTcpSocket::%s()", __FUNCTION__);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int len = 0;
|
int len = 0;
|
||||||
@@ -374,7 +376,7 @@ bool cIptvTcpSocket::ReadChar(char *bufferAddrP, unsigned int timeoutMsP)
|
|||||||
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
|
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
|
||||||
// Error out if socket not initialized
|
// Error out if socket not initialized
|
||||||
if (socketDescM <= 0) {
|
if (socketDescM <= 0) {
|
||||||
error("Invalid socket in cIptvTcpSocket::%s()\n", __FUNCTION__);
|
error("Invalid socket in cIptvTcpSocket::%s()", __FUNCTION__);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
socklen_t addrlen = sizeof(sockAddrM);
|
socklen_t addrlen = sizeof(sockAddrM);
|
||||||
@@ -399,7 +401,7 @@ bool cIptvTcpSocket::Write(const char *bufferAddrP, unsigned int bufferLenP)
|
|||||||
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
|
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
|
||||||
// Error out if socket not initialized
|
// Error out if socket not initialized
|
||||||
if (socketDescM <= 0) {
|
if (socketDescM <= 0) {
|
||||||
error("Invalid socket in cIptvTcpSocket::%s()\n", __FUNCTION__);
|
error("Invalid socket in cIptvTcpSocket::%s()", __FUNCTION__);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ERROR_IF_RET(send(socketDescM, bufferAddrP, bufferLenP, 0) < 0, "send()", return false);
|
ERROR_IF_RET(send(socketDescM, bufferAddrP, bufferLenP, 0) < 0, "send()", return false);
|
||||||
|
|||||||
13
source.c
13
source.c
@@ -5,6 +5,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "source.h"
|
#include "source.h"
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ bool cIptvTransponderParameters::Parse(const char *strP)
|
|||||||
if (strP && *strP) {
|
if (strP && *strP) {
|
||||||
const char *delim = "|";
|
const char *delim = "|";
|
||||||
char *str = strdup(strP);
|
char *str = strdup(strP);
|
||||||
|
char *p = str;
|
||||||
char *saveptr = NULL;
|
char *saveptr = NULL;
|
||||||
char *token = NULL;
|
char *token = NULL;
|
||||||
bool found_s = false;
|
bool found_s = false;
|
||||||
@@ -70,7 +72,7 @@ bool cIptvTransponderParameters::Parse(const char *strP)
|
|||||||
++data;
|
++data;
|
||||||
if (data && (*data == '=')) {
|
if (data && (*data == '=')) {
|
||||||
++data;
|
++data;
|
||||||
switch (*token) {
|
switch (toupper(*token)) {
|
||||||
case 'S':
|
case 'S':
|
||||||
sidScanM = (int)strtol(data, (char **)NULL, 10);
|
sidScanM = (int)strtol(data, (char **)NULL, 10);
|
||||||
found_s = true;
|
found_s = true;
|
||||||
@@ -119,9 +121,9 @@ bool cIptvTransponderParameters::Parse(const char *strP)
|
|||||||
if (found_s && found_p && found_f && found_u && found_a)
|
if (found_s && found_p && found_f && found_u && found_a)
|
||||||
result = true;
|
result = true;
|
||||||
else
|
else
|
||||||
error("Invalid channel parameters: %s\n", str);
|
error("Invalid channel parameters: %s", p);
|
||||||
|
|
||||||
free(str);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (result);
|
return (result);
|
||||||
@@ -163,9 +165,8 @@ void cIptvSourceParam::SetData(cChannel *channelP)
|
|||||||
void cIptvSourceParam::GetData(cChannel *channelP)
|
void cIptvSourceParam::GetData(cChannel *channelP)
|
||||||
{
|
{
|
||||||
debug("cIptvSourceParam::%s(%s)", __FUNCTION__, channelP->Parameters());
|
debug("cIptvSourceParam::%s(%s)", __FUNCTION__, channelP->Parameters());
|
||||||
dataM.SetTransponderData(channelP->Source(), channelP->Frequency(), dataM.Srate(), itpM.ToString(Source()), true);
|
channelP->SetTransponderData(channelP->Source(), channelP->Frequency(), dataM.Srate(), itpM.ToString(Source()), true);
|
||||||
dataM.SetId(nidM, tidM, channelP->Sid(), ridM);
|
channelP->SetId(nidM, tidM, channelP->Sid(), ridM);
|
||||||
*channelP = dataM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cOsdItem *cIptvSourceParam::GetOsdItem(void)
|
cOsdItem *cIptvSourceParam::GetOsdItem(void)
|
||||||
|
|||||||
2
source.h
2
source.h
@@ -20,7 +20,7 @@ private:
|
|||||||
int sidScanM;
|
int sidScanM;
|
||||||
int pidScanM;
|
int pidScanM;
|
||||||
int protocolM;
|
int protocolM;
|
||||||
char addressM[NAME_MAX];
|
char addressM[NAME_MAX + 1];
|
||||||
int parameterM;
|
int parameterM;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ cString cIptvSectionStatistics::GetSectionStatistic()
|
|||||||
|
|
||||||
void cIptvSectionStatistics::AddSectionStatistic(long bytesP, long callsP)
|
void cIptvSectionStatistics::AddSectionStatistic(long bytesP, long callsP)
|
||||||
{
|
{
|
||||||
//debug("cIptvSectionStatistics::%s(%ld, %ld)", __FUNCTION__, bytesP, callsP);
|
//debug("cIptvSectionStatistics::%s(%ld, %ld)", __FUNCTION__, bytesP, callsP);
|
||||||
cMutexLock MutexLock(&mutexM);
|
cMutexLock MutexLock(&mutexM);
|
||||||
filteredDataM += bytesP;
|
filteredDataM += bytesP;
|
||||||
numberOfCallsM += callsP;
|
numberOfCallsM += callsP;
|
||||||
|
|||||||
20
streamer.c
20
streamer.c
@@ -5,15 +5,13 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <vdr/thread.h>
|
|
||||||
#include <vdr/ringbuffer.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "streamer.h"
|
#include "streamer.h"
|
||||||
|
|
||||||
cIptvStreamer::cIptvStreamer(cRingBufferLinear* ringBufferP, unsigned int packetLenP)
|
cIptvStreamer::cIptvStreamer(cIptvDeviceIf &deviceP, unsigned int packetLenP)
|
||||||
: cThread("IPTV streamer"),
|
: cThread("IPTV streamer"),
|
||||||
ringBufferM(ringBufferP),
|
sleepM(),
|
||||||
|
deviceM(&deviceP),
|
||||||
packetBufferLenM(packetLenP),
|
packetBufferLenM(packetLenP),
|
||||||
protocolM(NULL)
|
protocolM(NULL)
|
||||||
{
|
{
|
||||||
@@ -32,7 +30,6 @@ cIptvStreamer::~cIptvStreamer()
|
|||||||
// Close the protocol
|
// Close the protocol
|
||||||
Close();
|
Close();
|
||||||
protocolM = NULL;
|
protocolM = NULL;
|
||||||
ringBufferM = NULL;
|
|
||||||
// Free allocated memory
|
// Free allocated memory
|
||||||
free(packetBufferM);
|
free(packetBufferM);
|
||||||
}
|
}
|
||||||
@@ -45,15 +42,12 @@ void cIptvStreamer::Action(void)
|
|||||||
// Do the thread loop
|
// Do the thread loop
|
||||||
while (packetBufferM && Running()) {
|
while (packetBufferM && Running()) {
|
||||||
int length = -1;
|
int length = -1;
|
||||||
if (protocolM)
|
unsigned int size = min(deviceM->CheckData(), packetBufferLenM);
|
||||||
length = protocolM->Read(packetBufferM, min((unsigned int)ringBufferM->Free(), packetBufferLenM));
|
if (protocolM && (size > 0))
|
||||||
|
length = protocolM->Read(packetBufferM, size);
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
AddStreamerStatistic(length);
|
AddStreamerStatistic(length);
|
||||||
if (ringBufferM) {
|
deviceM->WriteData(packetBufferM, length);
|
||||||
int p = ringBufferM->Put(packetBufferM, length);
|
|
||||||
if (p != length)
|
|
||||||
ringBufferM->ReportOverflow(length - p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sleepM.Wait(10); // to avoid busy loop and reduce cpu load
|
sleepM.Wait(10); // to avoid busy loop and reduce cpu load
|
||||||
|
|||||||
@@ -11,15 +11,15 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include <vdr/thread.h>
|
#include <vdr/thread.h>
|
||||||
#include <vdr/ringbuffer.h>
|
|
||||||
|
|
||||||
|
#include "deviceif.h"
|
||||||
#include "protocolif.h"
|
#include "protocolif.h"
|
||||||
#include "statistics.h"
|
#include "statistics.h"
|
||||||
|
|
||||||
class cIptvStreamer : public cThread, public cIptvStreamerStatistics {
|
class cIptvStreamer : public cThread, public cIptvStreamerStatistics {
|
||||||
private:
|
private:
|
||||||
cRingBufferLinear* ringBufferM;
|
|
||||||
cCondWait sleepM;
|
cCondWait sleepM;
|
||||||
|
cIptvDeviceIf* deviceM;
|
||||||
unsigned char* packetBufferM;
|
unsigned char* packetBufferM;
|
||||||
unsigned int packetBufferLenM;
|
unsigned int packetBufferLenM;
|
||||||
cIptvProtocolIf* protocolM;
|
cIptvProtocolIf* protocolM;
|
||||||
@@ -28,7 +28,7 @@ protected:
|
|||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cIptvStreamer(cRingBufferLinear* ringBufferP, unsigned int packetLenP);
|
cIptvStreamer(cIptvDeviceIf &deviceP, unsigned int packetLenP);
|
||||||
virtual ~cIptvStreamer();
|
virtual ~cIptvStreamer();
|
||||||
bool Set(const char* locationP, const int parameterP, const int indexP, cIptvProtocolIf* protocolP);
|
bool Set(const char* locationP, const int parameterP, const int indexP, cIptvProtocolIf* protocolP);
|
||||||
bool Open(void);
|
bool Open(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user