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

Compare commits

...

21 Commits

Author SHA1 Message Date
Rolf Ahrenberg
dc64c044a1 Fixed installation target bugs (Thanks to Alexander Grothe). 2014-03-09 16:08:44 +02:00
Rolf Ahrenberg
3dcbff0a71 Updated translation files and version number. 2014-01-19 00:20:08 +02:00
Rolf Ahrenberg
5aefd40d65 Updated translation files and HISTORY. 2014-01-14 18:42:06 +02:00
Rolf Ahrenberg
6b0337d078 Added initial support for cDevice::MaySwitchTransponder(). 2014-01-14 18:40:54 +02:00
Rolf Ahrenberg
9e6d784aec Shutdown devices already in cPluginManager::Stop(). 2014-01-14 18:30:14 +02:00
Rolf Ahrenberg
3f3ba2ad1a Fixed a typo. 2014-01-11 00:35:12 +02:00
Rolf Ahrenberg
a4b9570ae6 Updated copyright headers. 2014-01-11 00:24:31 +02:00
Rolf Ahrenberg
767b1bdac7 Added support for cDevice::GetCurrentlyTunedTransponder(). 2014-01-10 23:59:26 +02:00
Rolf Ahrenberg
6f648401a4 Updated translation files and HISTORY. 2014-01-10 23:22:15 +02:00
Rolf Ahrenberg
b0995c9a9e Added support for cDevice::IsTunedToTransponder(). 2014-01-07 22:24:39 +02:00
Rolf Ahrenberg
412af81271 Fixed a mem leak and re-ordered pointer deletions. 2014-01-07 17:55:13 +02:00
Rolf Ahrenberg
43326bb4ea Whitespace cleanup. 2014-01-07 17:55:08 +02:00
Rolf Ahrenberg
555c08bd4d Updated Makefile. 2014-01-06 22:01:55 +02:00
Rolf Ahrenberg
5ad3103626 Fixed scan-build issues. 2014-01-02 21:47:43 +02:00
Rolf Ahrenberg
a4f0bdf737 Added missing CURL timeouts and improved section id scanner. 2014-01-02 21:35:38 +02:00
Rolf Ahrenberg
01b554a2de Updated README. 2013-04-01 23:11:38 +03:00
Rolf Ahrenberg
225b0cc297 Updated for vdr-2.0.0. 2013-04-01 22:36:41 +03:00
Rolf Ahrenberg
d940e616e3 Fixed and refactored the section filtering code, fixed a possible crash in the file protocol, and updated Makefile's install target. 2013-03-27 22:13:15 +02:00
Rolf Ahrenberg
f30817677e Fixed a nasty network byte order bug. 2013-03-17 01:18:58 +02:00
Rolf Ahrenberg
0c6257e4ca Added SetMenuCategory(mcSetupPlugins). 2013-03-13 23:33:37 +02:00
Rolf Ahrenberg
0caf746ef4 Enabled I/O throttling and tweaked buffer timeouts and tweaked max address string size to include a termination NULL byte. 2013-03-11 18:04:27 +02:00
32 changed files with 524 additions and 255 deletions

30
HISTORY
View File

@@ -199,3 +199,33 @@ VDR Plugin 'iptv' Revision History
- 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).

View File

@@ -28,10 +28,12 @@ GITTAG = $(shell git describe --always 2>/dev/null)
### The directory environment:
# 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)
LOCDIR = $(call PKGCFG,locdir)
PLGCFG = $(call PKGCFG,plgcfg)
RESDIR = $(call PKGCFG,resdir)
CFGDIR = $(call PKGCFG,configdir)
#
TMPDIR ?= /tmp
@@ -144,7 +146,12 @@ endif
install-lib: $(SOFILE)
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
@-rm -rf $(TMPDIR)/$(ARCHIVE)

28
README
View File

@@ -41,14 +41,8 @@ MP3 radio streams, mms video streams and so on.
Installation:
cd /put/your/path/here/VDR/PLUGINS/src
tar -xzf /put/your/path/here/vdr-iptv-X.Y.Z.tgz
ln -s iptv-X.Y.Z iptv
cd /put/your/path/here/VDR
cp -R PLUGINS/src/iptv/iptv /path/to/vdrresource/plugins/
make
make plugins
./vdr -P iptv
make -C iptv-X.Y.Z install
Setup menu:
@@ -89,11 +83,11 @@ Configuration:
- channels.conf
TV4;IPTV:60:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:6:0:0:0
TV3;IPTV:50:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:5:0:0:0
TV2;IPTV:40:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:4:0:0:0
TV1;IPTV:30:S=1|P=0|F=CURL|U=http%3A//foo%3Abar@127.0.0.1%3A3000/TS/2|A=0:I:0:512:650:2321:0:3:0:0:0
TV1;IPTV:20:S=1|P=0|F=UDP|U=127.0.0.1@127.0.0.1|A=1234:I:0:512:650:2321:0:2:0:0:0
TV6;IPTV:60:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:6: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
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
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
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
^ ^ ^ ^ ^ ^ ^
| | | | | | Source type ("I")
@@ -171,8 +165,7 @@ Notes:
"disable_ca_updates" patch to the VDR in order to get rid of "Channel not
available" messages.
- EIT scanning functionality can be disabled for all IPTV channels by applying
the "disable_eitscan" patch to the VDR.
- EIT scanning functionality is disabled by default.
- Section id and pid scanners should be disabled after the correct data is
found. This can be made via VDR's channel editor.
@@ -186,7 +179,12 @@ Notes:
netrc configuration file for authentication:
$(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:
- The IPTV section filtering code is derived from Linux kernel.

View File

@@ -44,7 +44,8 @@
do { \
if (exp) { \
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; \
ret; \
} \

View File

@@ -19,6 +19,7 @@ cIptvConfig::cIptvConfig(void)
for (unsigned int i = 0; i < ARRAY_SIZE(disabledFiltersM); ++i)
disabledFiltersM[i] = -1;
memset(configDirectoryM, 0, sizeof(configDirectoryM));
memset(resourceDirectoryM, 0, sizeof(resourceDirectoryM));
}
unsigned int cIptvConfig::GetDisabledFiltersCount(void) const
@@ -45,3 +46,9 @@ void cIptvConfig::SetConfigDirectory(const char *directoryP)
debug("cIptvConfig::%s(%s)", __FUNCTION__, directoryP);
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");
}

View File

@@ -21,6 +21,7 @@ private:
unsigned int sectionFilteringM;
int disabledFiltersM[SECTION_FILTER_TABLE_SIZE];
char configDirectoryM[PATH_MAX];
char resourceDirectoryM[PATH_MAX];
public:
cIptvConfig();
@@ -30,6 +31,7 @@ public:
unsigned int GetUseBytes(void) const { return useBytesM; }
unsigned int GetSectionFiltering(void) const { return sectionFilteringM; }
const char *GetConfigDirectory(void) const { return configDirectoryM; }
const char *GetResourceDirectory(void) const { return resourceDirectoryM; }
unsigned int GetDisabledFiltersCount(void) const;
int GetDisabledFilters(unsigned int indexP) const;
void SetTsBufferSize(unsigned int sizeP) { tsBufferSizeM = sizeP; }
@@ -39,6 +41,7 @@ public:
void SetSectionFiltering(unsigned int onOffP) { sectionFilteringM = onOffP; }
void SetDisabledFilters(unsigned int indexP, int numberP);
void SetConfigDirectory(const char *directoryP);
void SetResourceDirectory(const char *directoryP);
};
extern cIptvConfig IptvConfig;

210
device.c
View File

@@ -20,25 +20,27 @@ cIptvDevice::cIptvDevice(unsigned int indexP)
isOpenDvrM(false),
sidScanEnabledM(false),
pidScanEnabledM(false),
channelIdM(tChannelID::InvalidID)
channelM()
{
unsigned int bufsize = (unsigned int)MEGABYTE(IptvConfig.GetTsBufferSize());
bufsize -= (bufsize % TS_SIZE);
isyslog("creating IPTV device %d (CardIndex=%d)", deviceIndexM, CardIndex());
tsBufferM = new cRingBufferLinear(bufsize + 1, TS_SIZE, false,
*cString::sprintf("IPTV %d", deviceIndexM));
tsBufferM->SetTimeouts(10, 10);
*cString::sprintf("IPTV TS %d", deviceIndexM));
if (tsBufferM) {
tsBufferM->SetTimeouts(100, 100);
tsBufferM->SetIoThrottle();
pIptvStreamerM = new cIptvStreamer(*this, tsBufferM->Free());
}
ResetBuffering();
pUdpProtocolM = new cIptvProtocolUdp();
pCurlProtocolM = new cIptvProtocolCurl();
pHttpProtocolM = new cIptvProtocolHttp();
pFileProtocolM = new cIptvProtocolFile();
pExtProtocolM = new cIptvProtocolExt();
pIptvStreamerM = new cIptvStreamer(tsBufferM, (100 * TS_SIZE));
pPidScannerM = new cPidScanner();
// Initialize filter pointers
memset(secFiltersM, 0, sizeof(secFiltersM));
// Start section handler for iptv device
pIptvSectionM = new cIptvSectionFilterHandler(deviceIndexM, bufsize + 1);
StartSectionHandler();
// Sid scanner must be created after the section handler
AttachFilter(pSidScannerM = new cSidScanner());
@@ -56,21 +58,18 @@ cIptvDevice::cIptvDevice(unsigned int indexP)
cIptvDevice::~cIptvDevice()
{
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
DELETE_POINTER(pIptvStreamerM);
DELETE_POINTER(pUdpProtocolM);
DELETE_POINTER(pCurlProtocolM);
DELETE_POINTER(pHttpProtocolM);
DELETE_POINTER(pFileProtocolM);
DELETE_POINTER(pExtProtocolM);
DELETE_POINTER(tsBufferM);
DELETE_POINTER(pPidScannerM);
DELETE_POINTER(pSidScannerM);
// Stop section handler of iptv device
StopSectionHandler();
// Destroy all filters
cMutexLock MutexLock(&mutexM);
for (int i = 0; i < eMaxSecFilterCount; ++i)
DeleteFilter(i);
DELETE_POINTER(pIptvSectionM);
DELETE_POINTER(pSidScannerM);
DELETE_POINTER(pPidScannerM);
DELETE_POINTER(pIptvStreamerM);
DELETE_POINTER(pExtProtocolM);
DELETE_POINTER(pFileProtocolM);
DELETE_POINTER(pHttpProtocolM);
DELETE_POINTER(pCurlProtocolM);
DELETE_POINTER(pUdpProtocolM);
DELETE_POINTER(tsBufferM);
// Close dvr fifo
if (dvrFdM >= 0) {
int fd = dvrFdM;
@@ -92,6 +91,15 @@ bool cIptvDevice::Initialize(unsigned int deviceCountP)
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 count = 0;
@@ -107,7 +115,7 @@ cIptvDevice *cIptvDevice::GetIptvDevice(int cardIndexP)
{
//debug("cIptvDevice::%s(%d)", __FUNCTION__, cardIndexP);
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);
return IptvDevicesS[i];
}
@@ -135,20 +143,7 @@ cString cIptvDevice::GetPidsInformation(void)
cString cIptvDevice::GetFiltersInformation(void)
{
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
unsigned int count = 0;
cString s("Active section filters:\n");
// loop through active section filters
cMutexLock MutexLock(&mutexM);
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (secFiltersM[i]) {
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
*secFiltersM[i]->GetSectionStatistic(), secFiltersM[i]->GetPid(),
id_pid(secFiltersM[i]->GetPid()));
if (++count > IPTV_STATS_ACTIVE_FILTERS_COUNT)
break;
}
}
return s;
return cString::sprintf("Active section filters:\n%s", pIptvSectionM ? *pIptvSectionM->GetInformation() : "");
}
cString cIptvDevice::GetInformation(unsigned int pageP)
@@ -183,7 +178,7 @@ cString cIptvDevice::GetInformation(unsigned int pageP)
cString cIptvDevice::DeviceType(void) const
{
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
return "IPTV";
}
@@ -228,7 +223,7 @@ bool cIptvDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool
if (channelP && ProvidesTransponder(channelP)) {
result = hasPriority;
if (Receiving()) {
if (channelP->GetChannelID() == channelIdM)
if (channelP->GetChannelID() == channelM.GetChannelID())
result = true;
else
needsDetachReceivers = Receiving();
@@ -249,6 +244,21 @@ int cIptvDevice::NumProvidedSystems(void) const
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)
{
cIptvProtocolIf *protocol;
@@ -284,11 +294,11 @@ bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
sidScanEnabledM = itp.SidScan() ? true : false;
pidScanEnabledM = itp.PidScan() ? true : false;
if (pIptvStreamerM->Set(itp.Address(), itp.Parameter(), deviceIndexM, protocol)) {
channelIdM = channelP->GetChannelID();
channelM = *channelP;
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
pSidScannerM->SetChannel(channelIdM);
pSidScannerM->SetChannel(channelM.GetChannelID());
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->SetChannel(channelIdM);
pPidScannerM->SetChannel(channelM.GetChannelID());
}
return true;
}
@@ -299,80 +309,19 @@ bool cIptvDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
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)
{
// Check if disabled by user
if (!IptvConfig.GetSectionFiltering())
return -1;
// Lock
cMutexLock MutexLock(&mutexM);
// Blacklist check, refuse certain filters
if (IsBlackListed(pidP, tidP, maskP))
return -1;
// 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
//debug("cIptvDevice::%s(%d): pid=%d tid=%d mask=%d", __FUNCTION__, deviceIndexM, pidP, tidP, maskP);
if (pIptvSectionM && IptvConfig.GetSectionFiltering())
return pIptvSectionM->Open(pidP, tidP, maskP);
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)
{
// Lock
cMutexLock MutexLock(&mutexM);
// ... and load
if (secFiltersM[handleP]) {
//debug("cIptvDevice::%s(%d): handle=%d", __FUNCTION__, deviceIndexM, handleP);
DeleteFilter(handleP);
}
//debug("cIptvDevice::%s(%d): handle=%d", __FUNCTION__, deviceIndexM, handleP);
if (pIptvSectionM)
pIptvSectionM->Close(handleP);
}
bool cIptvDevice::OpenDvr(void)
@@ -385,6 +334,8 @@ bool cIptvDevice::OpenDvr(void)
pIptvStreamerM->Open();
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
pSidScannerM->Open();
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->Open();
isOpenDvrM = true;
return true;
}
@@ -392,7 +343,9 @@ bool cIptvDevice::OpenDvr(void)
void cIptvDevice::CloseDvr(void)
{
debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
if (sidScanEnabledM && pSidScannerM )
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->Close();
if (sidScanEnabledM && pSidScannerM)
pSidScannerM->Close();
if (pIptvStreamerM)
pIptvStreamerM->Close();
@@ -430,10 +383,36 @@ bool cIptvDevice::IsBuffering(void) const
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)
{
//debug("cIptvDevice::%s(%d)", __FUNCTION__, deviceIndexM);
if (tsBufferM && !IsBuffering()) {
if (isOpenDvrM && tsBufferM && !IsBuffering()) {
if (isPacketDeliveredM) {
tsBufferM->Del(TS_SIZE);
isPacketDeliveredM = false;
@@ -451,26 +430,13 @@ bool cIptvDevice::GetTSPacket(uchar *&Data)
}
}
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;
}
isPacketDeliveredM = true;
Data = p;
// Update pid statistics
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;
}
}

View File

@@ -10,6 +10,7 @@
#include <vdr/device.h>
#include "common.h"
#include "deviceif.h"
#include "protocoludp.h"
#include "protocolcurl.h"
#include "protocolhttp.h"
@@ -21,19 +22,17 @@
#include "sidscanner.h"
#include "statistics.h"
class cIptvDevice : public cDevice, public cIptvPidStatistics, public cIptvBufferStatistics {
class cIptvDevice : public cDevice, public cIptvPidStatistics, public cIptvBufferStatistics, public cIptvDeviceIf {
// static ones
public:
static unsigned int deviceCount;
static bool Initialize(unsigned int DeviceCount);
static void Shutdown(void);
static unsigned int Count(void);
static cIptvDevice *GetIptvDevice(int CardIndex);
// private parts
private:
enum {
eMaxSecFilterCount = 32
};
unsigned int deviceIndexM;
int dvrFdM;
bool isPacketDeliveredM;
@@ -42,17 +41,17 @@ private:
bool pidScanEnabledM;
cRingBufferLinear *tsBufferM;
mutable int tsBufferPrefillM;
tChannelID channelIdM;
cChannel channelM;
cIptvProtocolUdp *pUdpProtocolM;
cIptvProtocolCurl *pCurlProtocolM;
cIptvProtocolHttp *pHttpProtocolM;
cIptvProtocolFile *pFileProtocolM;
cIptvProtocolExt *pExtProtocolM;
cIptvStreamer *pIptvStreamerM;
cIptvSectionFilterHandler *pIptvSectionM;
cPidScanner *pPidScannerM;
cSidScanner *pSidScannerM;
cMutex mutexM;
cIptvSectionFilter *secFiltersM[eMaxSecFilterCount];
// constructor & destructor
public:
@@ -74,8 +73,6 @@ private:
private:
void ResetBuffering(void);
bool IsBuffering(void) const;
bool DeleteFilter(unsigned int indexP);
bool IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const;
// for channel info
public:
@@ -91,6 +88,10 @@ public:
virtual bool ProvidesChannel(const cChannel *channelP, int priorityP = -1, bool *needsDetachReceiversP = NULL) const;
virtual bool ProvidesEIT(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:
virtual bool SetChannelDevice(const cChannel *channelP, bool liveViewP);
@@ -104,7 +105,6 @@ protected:
// for section filtering
public:
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);
// for transponder lock
@@ -114,6 +114,11 @@ public:
// for common interface
public:
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

23
deviceif.h Normal file
View 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
View File

@@ -13,15 +13,15 @@
#include "device.h"
#include "iptvservice.h"
#if defined(APIVERSNUM) && APIVERSNUM < 10738
#error "VDR-1.7.38 API version or greater is required!"
#if defined(APIVERSNUM) && APIVERSNUM < 20000
#error "VDR-2.0.0 API version or greater is required!"
#endif
#ifndef GITVERSION
#define GITVERSION ""
#endif
const char VERSION[] = "1.2.1" GITVERSION;
const char VERSION[] = "2.0.3" GITVERSION;
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
class cPluginIptv : public cPlugin {
@@ -99,7 +99,8 @@ bool cPluginIptv::Initialize(void)
{
debug("cPluginIptv::%s()", __FUNCTION__);
// 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);
}
@@ -118,6 +119,7 @@ void cPluginIptv::Stop(void)
{
debug("cPluginIptv::%s()", __FUNCTION__);
// Stop any background activities the plugin is performing.
cIptvDevice::Shutdown();
curl_global_cleanup();
}

View File

@@ -39,7 +39,6 @@ void cPidScanner::SetChannel(const tChannelID &channelIdP)
aPidM = 0xFFFF;
numApidsM = 0;
processM = true;
timeoutM.Set(PIDSCANNER_TIMEOUT_IN_MS);
}
void cPidScanner::Process(const uint8_t* bufP)
@@ -56,7 +55,7 @@ void cPidScanner::Process(const uint8_t* bufP)
// Verify TS packet
if (bufP[0] != 0x47) {
error("Not TS packet: 0x%X\n", bufP[0]);
error("Not TS packet: 0x%02X", bufP[0]);
return;
}

View File

@@ -13,6 +13,12 @@
class cPidScanner {
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;
tChannelID channelIdM;
bool processM;
@@ -26,6 +32,8 @@ public:
~cPidScanner();
void SetChannel(const tChannelID &channelIdP);
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

View File

@@ -1,14 +1,14 @@
# 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.
# Tobias <vdr@e-tobi.net>, 2007.
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Project-Id-Version: vdr-iptv 2.0.3\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
"Last-Translator: Tobias Grimm <tg@e-tobi.net>\n"
"Language-Team: German <vdr@linuxtv.org>\n"
"Language: de\n"

View File

@@ -1,14 +1,14 @@
# 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.
# Rolf Ahrenberg
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Project-Id-Version: vdr-iptv 2.0.3\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
"Last-Translator: Rolf Ahrenberg\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n"
"Language: fi\n"

View File

@@ -1,15 +1,15 @@
# 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.
# Bruno ROUSSEL <bruno.roussel@free.fr>, 2007.
# NIVAL Michaël <mnival@club-internet.fr>, 2008.
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Project-Id-Version: vdr-iptv 2.0.3\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
"Last-Translator: NIVAL Michaël <mnival@club-internet.fr>\n"
"Language-Team: French <vdr@linuxtv.org>\n"
"Language: fr\n"

View File

@@ -1,14 +1,14 @@
# 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.
# Diego Pierotto <vdr-italian@tiscali.it>, 2008.
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Project-Id-Version: vdr-iptv 2.0.3\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n"
"Language: it\n"

View File

@@ -1,14 +1,14 @@
# 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.
# Carel, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Project-Id-Version: vdr-iptv 2.0.3\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
"Last-Translator: Carel\n"
"Language-Team: Dutch <vdr@linuxtv.org>\n"
"Language: nl\n"

View File

@@ -1,14 +1,14 @@
# 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.
# Alexander Gross <Bikalexander@gmail.com>, 2008.
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 1.2.1\n"
"Project-Id-Version: vdr-iptv 2.0.3\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"POT-Creation-Date: 2014-03-09 03:09+0200\n"
"PO-Revision-Date: 2014-03-09 03:09+0200\n"
"Last-Translator: Alexander Gross <Bikalexander@gmail.com>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n"
"Language: ru\n"

View File

@@ -9,14 +9,18 @@
#include "config.h"
#include "protocolcurl.h"
#ifdef CURLOPT_RTSPHEADER
#define USE_RTSP
#endif
#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); \
error("curl_easy_setopt(%s, %s) failed: %s (%d)", #Y, #Z, curl_easy_strerror(res), res); \
}
#define iptv_curl_easy_perform(X) \
if ((res = curl_easy_perform((X))) != CURLE_OK) { \
error("curl_easy_perform(%s) failed: %d\n", #X, res); \
error("curl_easy_perform() failed: %s (%d)", curl_easy_strerror(res), res); \
}
cIptvProtocolCurl::cIptvProtocolCurl()
@@ -34,8 +38,10 @@ cIptvProtocolCurl::cIptvProtocolCurl()
pausedM(false)
{
debug("cIptvProtocolCurl::%s()", __FUNCTION__);
if (ringBufferM)
if (ringBufferM) {
ringBufferM->SetTimeouts(100, 0);
ringBufferM->SetIoThrottle();
}
Connect();
}
@@ -67,7 +73,7 @@ size_t cIptvProtocolCurl::WriteRtspCallback(void *ptrP, size_t sizeP, size_t nme
//debug("cIptvProtocolCurl::%s(%zu)", __FUNCTION__, len);
// 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];
if (length > 3) {
// Skip interleave header
@@ -208,7 +214,7 @@ unsigned char *cIptvProtocolCurl::GetData(int &lenP)
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);
lenP = 0;
return NULL;
@@ -258,7 +264,8 @@ bool cIptvProtocolCurl::Connect()
iptv_curl_easy_setopt(handleM, CURLOPT_NETRC, (long)CURL_NETRC_OPTIONAL);
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);
// Set user-agent
@@ -272,6 +279,7 @@ bool cIptvProtocolCurl::Connect()
// Protocol specific initializations
switch (modeM) {
#ifdef USE_RTSP
case eModeRtsp:
{
cString uri, control, transport, range;
@@ -315,7 +323,7 @@ bool cIptvProtocolCurl::Connect()
iptv_curl_easy_perform(handleM);
}
break;
#endif
case eModeHttp:
case eModeHttps:
{
@@ -338,6 +346,10 @@ bool cIptvProtocolCurl::Connect()
break;
case eModeFile:
// Set timeout
iptv_curl_easy_setopt(handleM, CURLOPT_TIMEOUT_MS, 10L);
break;
case eModeUnknown:
default:
break;
@@ -364,6 +376,7 @@ bool cIptvProtocolCurl::Disconnect()
if (handleM && multiM) {
// Mode specific tricks
switch (modeM) {
#ifdef USE_RTSP
case eModeRtsp:
{
CURLcode res = CURLE_OK;
@@ -375,7 +388,7 @@ bool cIptvProtocolCurl::Disconnect()
rtspControlM = "";
}
break;
#endif
case eModeHttp:
case eModeHttps:
case eModeFile:
@@ -422,6 +435,7 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
// Fill up the buffer
if (handleM && multiM) {
switch (modeM) {
#ifdef USE_RTSP
case eModeRtsp:
{
cMutexLock MutexLock(&mutexM);
@@ -431,7 +445,7 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
// @todo - How to detect eof?
}
break;
#endif
case eModeFile:
case eModeHttp:
case eModeHttps:
@@ -444,9 +458,9 @@ int cIptvProtocolCurl::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
res = curl_multi_perform(multiM, &running_handles);
} while (res == CURLM_CALL_MULTI_PERFORM);
// Shall we continue filling up the buffer?
// Use 20% threshold before continuing to filling up the buffer.
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());
pausedM = false;

View File

@@ -158,7 +158,7 @@ bool cIptvProtocolExt::Set(const char* locationP, const int parameterP, const in
if (!isempty(locationP)) {
struct stat stbuf;
// 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)) {
error("Non-existent or relative path script '%s'", *scriptFileM);
return false;

View File

@@ -60,7 +60,7 @@ int cIptvProtocolFile::Read(unsigned char* bufferAddrP, unsigned int bufferLenP)
{
//debug("cIptvProtocolFile::%s()", __FUNCTION__);
// Check errors
if (ferror(fileStreamM)) {
if (!fileStreamM || ferror(fileStreamM)) {
debug("cIptvProtocolFile::%s(): stream error", __FUNCTION__);
return -1;
}

View File

@@ -113,13 +113,13 @@ bool cIptvProtocolHttp::GetHeaderLine(char* destP, unsigned int destLenP,
++bufptr;
// Check that buffer won't be exceeded
if (recvLenP >= destLenP) {
error("Header wouldn't fit into buffer\n");
error("Header wouldn't fit into buffer");
recvLenP = 0;
return false;
}
}
else {
error("No HTTP response received in 500ms\n");
error("No HTTP response received in 500ms");
return false;
}
}
@@ -143,14 +143,14 @@ bool cIptvProtocolHttp::ProcessHeaders(void)
if (!GetHeaderLine(buf, sizeof(buf), lineLength))
return false;
if (!responseFound && sscanf(buf, fmt, &version, &response) != 2) {
error("Expected HTTP header not found\n");
error("Expected HTTP header not found");
continue;
}
else
responseFound = true;
// Allow only 'OK' and 'Partial Content'
if ((response != 200) && (response != 206)) {
error("Invalid HTTP response (%d): %s\n", response, buf);
error("Invalid HTTP response (%d): %s", response, buf);
return false;
}
}

View File

@@ -17,9 +17,9 @@ cIptvSectionFilter::cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t
secLenM(0),
tsFeedpM(0),
pidM(pidP),
devIdM(deviceIndexP)
deviceIndexM(deviceIndexP)
{
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, devIdM, pidM);
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
int i;
memset(secBufBaseM, 0, sizeof(secBufBaseM));
@@ -46,32 +46,32 @@ cIptvSectionFilter::cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t
}
doneqM = doneq ? 1 : 0;
// Create filtering buffer
ringbufferM = new cRingBufferLinear(KILOBYTE(128), 0, false, *cString::sprintf("IPTV SECTION %d/%d", devIdM, pidM));
if (ringbufferM)
ringbufferM->SetTimeouts(10, 10);
else
error("Failed to allocate buffer for section filter (device=%d pid=%d): ", devIdM, pidM);
// Create sockets
socketM[0] = socketM[1] = -1;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socketM) != 0) {
char tmp[64];
error("Opening section filter sockets failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
}
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()
{
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, devIdM, pidM);
DELETE_POINTER(ringbufferM);
//debug("cIptvSectionFilter::%s(%d, %d)", __FUNCTION__, deviceIndexM, pidM);
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;
}
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)
{
return (uint16_t)(3 + ((dataP[1] & 0x0f) << 8) + dataP[2]);
@@ -99,10 +99,10 @@ int cIptvSectionFilter::Filter(void)
if (doneqM && !neq)
return 0;
if (ringbufferM) {
int len = ringbufferM->Put(secBufM, secLenM);
if (len != secLenM)
ringbufferM->ReportOverflow(secLenM - len);
// There is no data in the read socket, more can be written
if ((socketM[0] >= 0) && (socketM[1] >= 0) /*&& !select_single_desc(socketM[0], 0, false)*/) {
ssize_t len = write(socketM[1], secBufM, secLenM);
ERROR_IF(len < 0, "write()");
// Update statistics
AddSectionStatistic(len, 1);
}
@@ -210,3 +210,183 @@ void cIptvSectionFilter::Process(const uint8_t* dataP)
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);
}
}

View File

@@ -35,7 +35,8 @@ private:
uint16_t tsFeedpM;
uint16_t pidM;
int devIdM;
int deviceIndexM;
int socketM[2];
uint8_t filterValueM[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 maskAndNotModeM[DMX_MAX_FILTER_SIZE];
cRingBufferLinear *ringbufferM;
inline uint16_t GetLength(const uint8_t *dataP);
void New(void);
int Filter(void);
@@ -57,8 +56,35 @@ public:
cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP);
virtual ~cIptvSectionFilter();
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; }
};
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

View File

@@ -124,6 +124,7 @@ cIptvPluginSetup::cIptvPluginSetup()
disabledFilterIndexesM[i] = IptvConfig.GetDisabledFilters(i);
disabledFilterNamesM[i] = tr(section_filter_table[i].description);
}
SetMenuCategory(mcSetupPlugins);
Setup();
SetHelp(NULL, NULL, NULL, trVDR("Button$Info"));
}

View File

@@ -45,7 +45,7 @@ void cSidScanner::Process(u_short pidP, u_char tidP, const u_char *dataP, int le
return;
if (channelIdM.Valid()) {
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);
if (!pat.CheckCRCAndParse())
return;
@@ -71,15 +71,21 @@ void cSidScanner::Process(u_short pidP, u_char tidP, const u_char *dataP, int le
if (ts.getTransportStreamId() != channelIdM.Tid()) {
debug("cSidScanner::%s(): tsid=%d", __FUNCTION__, 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
}
if (nit.getNetworkId() != channelIdM.Nid()) {
debug("cSidScanner::%s(): nid=%d\n", __FUNCTION__, ts.getTransportStreamId());
// fallback for network id if not found already
if (!nidFoundM && (nit.getNetworkId() != channelIdM.Nid())) {
debug("cSidScanner::%s(): nid=%d", __FUNCTION__, nit.getNetworkId());
newNid = nit.getNetworkId();
nidFoundM = true;
}
nidFoundM = true;
}
}
if ((newSid >= 0) || (newNid >= 0) || (newTid >= 0)) {

View File

@@ -93,7 +93,7 @@ bool cIptvSocket::CheckAddress(const char *addrP, in_addr_t *inAddrP)
{
if (inAddrP) {
// First try only the IP address
*inAddrP = htonl(inet_addr(addrP));
*inAddrP = inet_addr(addrP);
if (*inAddrP == htonl(INADDR_NONE)) {
debug("cIptvSocket::%s(%s): cannot convert to address", __FUNCTION__, addrP);
// It may be a host name, get the name
@@ -104,7 +104,7 @@ bool cIptvSocket::CheckAddress(const char *addrP, in_addr_t *inAddrP)
strerror_r(h_errno, tmp, sizeof(tmp)));
return false;
}
*inAddrP = htonl(inet_addr(*host->h_addr_list));
*inAddrP = inet_addr(*host->h_addr_list);
}
return true;
}
@@ -226,7 +226,7 @@ int cIptvUdpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
//debug("cIptvUdpSocket::%s()", __FUNCTION__);
// Error out if socket not initialized
if (socketDescM <= 0) {
error("Invalid socket in cIptvUdpSocket::%s()\n", __FUNCTION__);
error("Invalid socket in cIptvUdpSocket::%s()", __FUNCTION__);
return -1;
}
int len = 0;
@@ -359,7 +359,7 @@ int cIptvTcpSocket::Read(unsigned char *bufferAddrP, unsigned int bufferLenP)
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
// Error out if socket not initialized
if (socketDescM <= 0) {
error("Invalid socket in cIptvTcpSocket::%s()\n", __FUNCTION__);
error("Invalid socket in cIptvTcpSocket::%s()", __FUNCTION__);
return -1;
}
int len = 0;
@@ -376,7 +376,7 @@ bool cIptvTcpSocket::ReadChar(char *bufferAddrP, unsigned int timeoutMsP)
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
// Error out if socket not initialized
if (socketDescM <= 0) {
error("Invalid socket in cIptvTcpSocket::%s()\n", __FUNCTION__);
error("Invalid socket in cIptvTcpSocket::%s()", __FUNCTION__);
return false;
}
socklen_t addrlen = sizeof(sockAddrM);
@@ -401,7 +401,7 @@ bool cIptvTcpSocket::Write(const char *bufferAddrP, unsigned int bufferLenP)
//debug("cIptvTcpSocket::%s()", __FUNCTION__);
// Error out if socket not initialized
if (socketDescM <= 0) {
error("Invalid socket in cIptvTcpSocket::%s()\n", __FUNCTION__);
error("Invalid socket in cIptvTcpSocket::%s()", __FUNCTION__);
return false;
}
ERROR_IF_RET(send(socketDescM, bufferAddrP, bufferLenP, 0) < 0, "send()", return false);

View File

@@ -58,6 +58,7 @@ bool cIptvTransponderParameters::Parse(const char *strP)
if (strP && *strP) {
const char *delim = "|";
char *str = strdup(strP);
char *p = str;
char *saveptr = NULL;
char *token = NULL;
bool found_s = false;
@@ -120,9 +121,9 @@ bool cIptvTransponderParameters::Parse(const char *strP)
if (found_s && found_p && found_f && found_u && found_a)
result = true;
else
error("Invalid channel parameters: %s\n", str);
error("Invalid channel parameters: %s", p);
free(str);
free(p);
}
return (result);

View File

@@ -20,7 +20,7 @@ private:
int sidScanM;
int pidScanM;
int protocolM;
char addressM[NAME_MAX];
char addressM[NAME_MAX + 1];
int parameterM;
public:

View File

@@ -44,7 +44,7 @@ cString cIptvSectionStatistics::GetSectionStatistic()
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);
filteredDataM += bytesP;
numberOfCallsM += callsP;

View File

@@ -5,16 +5,13 @@
*
*/
#include <vdr/thread.h>
#include <vdr/ringbuffer.h>
#include "common.h"
#include "streamer.h"
cIptvStreamer::cIptvStreamer(cRingBufferLinear* ringBufferP, unsigned int packetLenP)
cIptvStreamer::cIptvStreamer(cIptvDeviceIf &deviceP, unsigned int packetLenP)
: cThread("IPTV streamer"),
ringBufferM(ringBufferP),
sleepM(),
deviceM(&deviceP),
packetBufferLenM(packetLenP),
protocolM(NULL)
{
@@ -33,7 +30,6 @@ cIptvStreamer::~cIptvStreamer()
// Close the protocol
Close();
protocolM = NULL;
ringBufferM = NULL;
// Free allocated memory
free(packetBufferM);
}
@@ -46,16 +42,12 @@ void cIptvStreamer::Action(void)
// Do the thread loop
while (packetBufferM && Running()) {
int length = -1;
unsigned int size = min((unsigned int)ringBufferM->Free(), packetBufferLenM);
unsigned int size = min(deviceM->CheckData(), packetBufferLenM);
if (protocolM && (size > 0))
length = protocolM->Read(packetBufferM, size);
if (length > 0) {
AddStreamerStatistic(length);
if (ringBufferM) {
int p = ringBufferM->Put(packetBufferM, length);
if (p != length)
ringBufferM->ReportOverflow(length - p);
}
deviceM->WriteData(packetBufferM, length);
}
else
sleepM.Wait(10); // to avoid busy loop and reduce cpu load

View File

@@ -11,15 +11,15 @@
#include <arpa/inet.h>
#include <vdr/thread.h>
#include <vdr/ringbuffer.h>
#include "deviceif.h"
#include "protocolif.h"
#include "statistics.h"
class cIptvStreamer : public cThread, public cIptvStreamerStatistics {
private:
cRingBufferLinear* ringBufferM;
cCondWait sleepM;
cIptvDeviceIf* deviceM;
unsigned char* packetBufferM;
unsigned int packetBufferLenM;
cIptvProtocolIf* protocolM;
@@ -28,7 +28,7 @@ protected:
virtual void Action(void);
public:
cIptvStreamer(cRingBufferLinear* ringBufferP, unsigned int packetLenP);
cIptvStreamer(cIptvDeviceIf &deviceP, unsigned int packetLenP);
virtual ~cIptvStreamer();
bool Set(const char* locationP, const int parameterP, const int indexP, cIptvProtocolIf* protocolP);
bool Open(void);