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

Compare commits

...

28 Commits

Author SHA1 Message Date
Rolf Ahrenberg
c975d458f5 Removed the disable_ca_updates patch. 2012-09-30 18:44:45 +03:00
Rolf Ahrenberg
11d864c16b Updated for vdr-1.7.30 and added support for source-specific multicasts (SSM).
Changed default external script directory from the configuration to the resource.
2012-09-30 18:42:40 +03:00
Rolf Ahrenberg
75d17b289b Added FreeBSD support (Thanks to Jürgen Lock). 2012-07-10 22:12:29 +03:00
Rolf Ahrenberg
547306b67c Updated translations. 2012-06-03 12:31:33 +03:00
Rolf Ahrenberg
763c83209d Updated README. 2012-06-03 12:24:09 +03:00
Rolf Ahrenberg
af85ef1703 Prepared for 1.0.0 release. 2012-06-02 21:57:29 +03:00
Rolf Ahrenberg
24ecadf414 Updated device selection. 2012-06-02 15:37:05 +03:00
Antti Seppälä
f4126b7e2c Try to read all data from udp socket at once
Existing implementation of udp read mechanism was giving up too easily.
Replaced it with version that tries more actively to drain the entire socket buffer.

Signed-off-by: Antti Seppälä <a.seppala@gmail.com>
2012-06-02 14:34:41 +03:00
Rolf Ahrenberg
c7cbde301b Tweaked reading from UDP sockets. 2012-06-02 13:32:28 +03:00
Rolf Ahrenberg
06506c41f6 Fixed TCP socket connection and silenced compilation warnings. 2012-04-26 21:32:20 +03:00
Rolf Ahrenberg
c33b05076a Incremented version number and updated HISTORY. 2012-04-26 00:29:46 +03:00
Rolf Ahrenberg
38bd9a21f6 Fixed response header parsing in HTTP protocol. 2012-04-26 00:27:24 +03:00
Rolf Ahrenberg
4c7b2ea69b Added cIptvDevice::DeviceType() and updated HISTORY. 2012-04-02 19:15:53 +03:00
Rolf Ahrenberg
c475b26515 Changed UDP protocol to always utilize the source address validation. 2012-04-02 00:35:32 +03:00
Rolf Ahrenberg
9145fb8bb8 Silenced a warning in the HTTP protocol. 2012-04-01 22:47:50 +03:00
Rolf Ahrenberg
7a8b2962b2 Added support for a service interface. 2012-04-01 22:46:08 +03:00
Rolf Ahrenberg
626d0402c2 Updated the disable ca updates patch. 2012-03-31 20:43:10 +03:00
Rolf Ahrenberg
b3b06e569f Fixed some channel switching bugs. 2012-03-31 20:32:04 +03:00
Rolf Ahrenberg
ff84c54d50 Fixed channel switching in UDP protocol. 2012-03-30 19:01:46 +03:00
Rolf Ahrenberg
4c39d8cf72 Updated device name. 2012-03-30 18:22:10 +03:00
Rolf Ahrenberg
0b790260fa Updated for vdr-1.7.27. 2012-03-25 16:42:06 +03:00
Rolf Ahrenberg
79113bbe09 Updated default CXXFLAGS. 2012-03-25 16:03:22 +03:00
Rolf Ahrenberg
30de763a4b Updated for vdr-1.7.26. 2012-03-10 23:22:20 +02:00
Rolf Ahrenberg
868865c47d Updated for vdr-1.7.25. 2012-03-03 15:18:59 +02:00
Rolf Ahrenberg
a73921041f Added a GIT tag into the version string. 2012-02-26 22:54:45 +02:00
Rolf Ahrenberg
49e3a9e23a Silenced compilation warnings. 2012-02-19 19:14:10 +02:00
Rolf Ahrenberg
73311873bc Updated Makefile. 2012-02-19 17:47:43 +02:00
Rolf Ahrenberg
816c357065 Updated the patch. 2012-02-19 17:43:43 +02:00
28 changed files with 492 additions and 257 deletions

30
HISTORY
View File

@@ -158,3 +158,33 @@ VDR Plugin 'iptv' Revision History
- Canonicalized the configuration directory.
- Added support for LDFLAGS.
- Added cppcheck target into Makefile.
2012-04-02: Version 0.5.1
- Updated for vdr-1.7.27.
- Updated Makefile.
- Silenced compilation warnings.
- Fixed channel switching bugs.
- Added support for a service interface.
- Changed UDP protocol to always utilize the source address
validation.
2012-04-26: Version 0.5.2
- Fixed connection problems in HTTP protocol.
2012-06-03: Version 1.0.0
- Optimized reading from UDP sockets.
- Fixed ProvidesChannel method.
2012-07-10: Version 1.0.1
- Added FreeBSD support (Thanks to Jürgen Lock).
2012-09-30: Version 1.1.0
- Updated for vdr-1.7.30.
- Added support for source-specific multicasts (SSM).
- Changed default external script directory from the
configuration to the resource.

View File

@@ -5,9 +5,15 @@
# Debugging on/off
#IPTV_DEBUG = 1
# Default shell for EXT protocol
#IPTV_EXTSHELL = /bin/bash
# Strip debug symbols? Set eg. to /bin/true if not
STRIP = strip
# Install command
INSTALL = cp --remove-destination
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name.
@@ -19,18 +25,19 @@ PLUGIN = iptv
### The version number of this plugin (taken from the main source file):
VERSION = $(shell grep 'const char VERSION\[\] *=' $(PLUGIN).c | awk '{ print $$5 }' | sed -e 's/[";]//g')
GITTAG = $(shell git describe --always 2>/dev/null)
### The C++ compiler and options:
CXX ?= g++
CXXFLAGS ?= -fPIC -g -O3 -Wall -Wextra -Wswitch-default -Wfloat-equal -Wundef -Wpointer-arith -Wconversion -Wcast-align -Wredundant-decls -Wno-unused-parameter -Woverloaded-virtual -Wno-parentheses
CXXFLAGS ?= -fPIC -g -O3 -Wall -Wextra -Wswitch-default -Wfloat-equal -Wundef -Wpointer-arith -Wconversion -Wcast-align -Wredundant-decls -Wno-unused-parameter -Werror=overloaded-virtual -Wno-parentheses
LDFLAGS ?= -Wl,--as-needed
### The directory environment:
VDRDIR = ../../..
LIBDIR = ../../lib
TMPDIR = /tmp
VDRDIR ?= ../../..
LIBDIR ?= ../../lib
TMPDIR ?= /tmp
### Make sure that necessary options are included:
@@ -59,6 +66,14 @@ ifdef IPTV_DEBUG
DEFINES += -DDEBUG
endif
ifdef IPTV_EXTSHELL
DEFINES += -DEXTSHELL='"$(IPTV_EXTSHELL)"'
endif
ifneq ($(strip $(GITTAG)),)
DEFINES += -DGITVERSION='"-GIT-$(GITTAG)"'
endif
.PHONY: all all-redirect
all-redirect: all
@@ -118,7 +133,7 @@ libvdr-$(PLUGIN).so: $(OBJS)
ifndef IPTV_DEBUG
@$(STRIP) $@
endif
@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
@$(INSTALL) $@ $(LIBDIR)/$@.$(APIVERSION)
dist: $(I18Npo) clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)

26
README
View File

@@ -44,7 +44,7 @@ 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/vdrconf/plugins/
cp -R PLUGINS/src/iptv/iptv /path/to/vdrresource/plugins/
make
make plugins
./vdr -P iptv
@@ -91,14 +91,15 @@ Configuration:
TV4;IPTV:40:S=1|P=0|F=EXT|U=iptvstream.sh|A=0:I:0:0:680:0:0:4:0:0:0
TV3;IPTV:30:S=0|P=1|F=FILE|U=/video/stream.ts|A=5:I:0:514:670:2321:0:3:0:0:0
TV2;IPTV:20:S=0|P=1|F=HTTP|U=127.0.0.1/TS/2|A=3000:I:0:513:660:2321:0:2:0:0:0
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1@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")
| | | | | Stream parameter (multicast port
| | | | | number, HTTP port number, file delay
| | | | | (ms), script parameter)
| | | | Stream address (multicast address, URL, file
| | | | location, script location)
| | | | Stream address (multicast source@group address,
| | | | URL, file location, script location)
| | | Stream protocol ("UDP", "HTTP", "FILE", "EXT")
| | Pid scanner ("0" disable, "1" enable)
| Section id (Sid/Nid/Tid) scanner ("0" disable, "1" enable)
@@ -106,19 +107,22 @@ Configuration:
- UDP multicast rules for iptables firewall
# Multicast UDP -packets
# Multicast UDP packets
iptables -A INPUT -i eth0 -p udp -d 224.0.0.0/4 --dport 1234 -j ACCEPT
# IGMP required by multicasts
iptables -A INPUT -i eth0 -p igmp -d 224.0.0.0/4 -j ACCEPT
# Default routing for multicast
route add -net 224.0.0.0 netmask 224.0.0.0 eth0
External streaming:
- To watch an externally received channel add an EXT entry to channels.conf
and specify a script name and parameter. The specified script is executed
from plugin configuration directory when VDR tunes to the channel. The
specified script parameter is passed to the script and it can be used to
select for example between different URLs.
from plugin resource directory when VDR tunes to the channel. The specified
script parameter is passed to the script and it can be used to select for
example between different URLs.
- When an EXT channel is opened the IPTV plugin opens an UDP listening port
on the localhost. The external script is responsible for supplying IPTV
@@ -161,12 +165,14 @@ Notes:
- EIT scanning functionality can be disabled for all IPTV channels by applying
the "disable_eitscan" patch to the VDR.
- Source address validation can be enabled for UDP protocol separated by
adding the source address after a ';' character: "U=239.192.0.1;239.192.0.2"
- Section id and pid scanners should be disabled after the correct data is
found. This can be made via VDR's channel editor.
- Source-specific multicast (SSM) can be enabled by defining both the source
address and the group address separated by a '@' character. This will use
IGMP v3 protocol:
"U=<source address>@<group address>"
Acknowledgements:
- The IPTV section filtering code is derived from Linux kernel.

View File

@@ -30,6 +30,8 @@
#define IPTV_DEVICE_INFO_GENERAL 1
#define IPTV_DEVICE_INFO_PIDS 2
#define IPTV_DEVICE_INFO_FILTERS 3
#define IPTV_DEVICE_INFO_PROTOCOL 4
#define IPTV_DEVICE_INFO_BITRATE 5
#define IPTV_STATS_ACTIVE_PIDS_COUNT 10
#define IPTV_STATS_ACTIVE_FILTERS_COUNT 10
@@ -37,13 +39,14 @@
#define SECTION_FILTER_TABLE_SIZE 7
#define ERROR_IF_FUNC(exp, errstr, func, ret) \
do { \
if (exp) { \
char tmp[64]; \
error(errstr": %s", strerror_r(errno, tmp, sizeof(tmp))); \
func; \
ret; \
} \
do { \
if (exp) { \
char tmp[64]; \
strerror_r(errno, tmp, sizeof(tmp)); \
error(errstr": %s", tmp); \
func; \
ret; \
} \
} while (0)

View File

@@ -42,6 +42,6 @@ void cIptvConfig::SetDisabledFilters(unsigned int Index, int Number)
void cIptvConfig::SetConfigDirectory(const char *directoryP)
{
debug("cIptvConfig::SetConfigDirectory(%s)", directoryP);
debug("cIptvConfig::SetConfigDirectory(%s)\n", directoryP);
ERROR_IF(!realpath(directoryP, configDirectory), "Cannot canonicalize configuration directory");
}

View File

@@ -123,7 +123,7 @@ cIptvDevice *cIptvDevice::GetIptvDevice(int CardIndex)
cString cIptvDevice::GetGeneralInformation(void)
{
//debug("cIptvDevice::GetGeneralInformation(%d)\n", deviceIndex);
return cString::sprintf("IPTV device: %d\nCardIndex: %d\n%s%s%sChannel: %s",
return cString::sprintf("IPTV device: %d\nCardIndex: %d\nStream: %s\nStream bitrate: %s\n%sChannel: %s",
deviceIndex, CardIndex(),
pIptvStreamer ? *pIptvStreamer->GetInformation() : "",
pIptvStreamer ? *pIptvStreamer->GetStreamerStatistic() : "",
@@ -169,6 +169,12 @@ cString cIptvDevice::GetInformation(unsigned int Page)
case IPTV_DEVICE_INFO_FILTERS:
info = GetFiltersInformation();
break;
case IPTV_DEVICE_INFO_PROTOCOL:
info = pIptvStreamer ? *pIptvStreamer->GetInformation() : "";
break;
case IPTV_DEVICE_INFO_BITRATE:
info = pIptvStreamer ? *pIptvStreamer->GetStreamerStatistic() : "";
break;
default:
info = cString::sprintf("%s%s%s",
*GetGeneralInformation(),
@@ -179,6 +185,18 @@ cString cIptvDevice::GetInformation(unsigned int Page)
return info;
}
cString cIptvDevice::DeviceType(void) const
{
debug("cIptvDevice::DeviceType(%d)\n", deviceIndex);
return "IPTV";
}
cString cIptvDevice::DeviceName(void) const
{
debug("cIptvDevice::DeviceName(%d)\n", deviceIndex);
return cString::sprintf("IPTV %d", deviceIndex);
}
int cIptvDevice::SignalStrength(void) const
{
debug("cIptvDevice::SignalStrength(%d)\n", deviceIndex);
@@ -206,11 +224,20 @@ bool cIptvDevice::ProvidesTransponder(const cChannel *Channel) const
bool cIptvDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
bool result = false;
bool needsDetachReceivers = Receiving(true) && Channel && !(Channel->GetChannelID() == channelId);
bool hasPriority = Priority == IDLEPRIORITY || Priority > this->Priority();
bool needsDetachReceivers = false;
debug("cIptvDevice::ProvidesChannel(%d)\n", deviceIndex);
if (ProvidesTransponder(Channel))
result = true;
if (Channel && ProvidesTransponder(Channel)) {
result = hasPriority;
if (Receiving()) {
if (Channel->GetChannelID() == channelId)
result = true;
else
needsDetachReceivers = Receiving();
}
}
if (NeedsDetachReceivers)
*NeedsDetachReceivers = needsDetachReceivers;
return result;
@@ -316,26 +343,37 @@ int cIptvDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (!secfilters[i]) {
//debug("cIptvDevice::OpenFilter(%d): Pid=%d Tid=%02X Mask=%02X Index=%d\n", deviceIndex, Pid, Tid, Mask, i);
secfilters[i] = new cIptvSectionFilter(deviceIndex, i, Pid, Tid, Mask);
return secfilters[i]->GetReadDesc();
secfilters[i] = new cIptvSectionFilter(deviceIndex, Pid, Tid, Mask);
if (secfilters[i])
return i;
break;
}
}
// No free filter slot found
return -1;
}
int cIptvDevice::ReadFilter(int Handle, void *Buffer, size_t Length)
{
// Lock
cMutexLock MutexLock(&mutex);
// ... and load
if (secfilters[Handle]) {
return secfilters[Handle]->Read(Buffer, Length);
//debug("cIptvDevice::ReadFilter(%d): %d %d\n", deviceIndex, Handle, Length);
}
return 0;
}
void cIptvDevice::CloseFilter(int Handle)
{
// Lock
cMutexLock MutexLock(&mutex);
// Search the filter for deletion
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (secfilters[i] && (Handle == secfilters[i]->GetReadDesc())) {
//debug("cIptvDevice::CloseFilter(%d): %d\n", deviceIndex, Handle);
DeleteFilter(i);
break;
}
}
// ... and load
if (secfilters[Handle]) {
//debug("cIptvDevice::CloseFilter(%d): %d\n", deviceIndex, Handle);
DeleteFilter(Handle);
}
}
bool cIptvDevice::OpenDvr(void)
@@ -368,6 +406,12 @@ bool cIptvDevice::HasLock(int TimeoutMs)
return (!IsBuffering());
}
bool cIptvDevice::HasInternalCam(void)
{
//debug("cIptvDevice::HasInternalCam(%d)\n", deviceIndex);
return true;
}
void cIptvDevice::ResetBuffering(void)
{
debug("cIptvDevice::ResetBuffering(%d)\n", deviceIndex);

View File

@@ -76,6 +76,9 @@ private:
bool IsBlackListed(u_short Pid, u_char Tid, u_char Mask) const;
// for channel info
public:
virtual cString DeviceType(void) const;
virtual cString DeviceName(void) const;
virtual int SignalStrength(void) const;
virtual int SignalQuality(void) const;
@@ -99,11 +102,16 @@ protected:
// for section filtering
public:
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
virtual int ReadFilter(int Handle, void *Buffer, size_t Length);
virtual void CloseFilter(int Handle);
// for transponder lock
public:
virtual bool HasLock(int);
// for common interface
public:
virtual bool HasInternalCam(void);
};
#endif // __IPTV_DEVICE_H

27
iptv.c
View File

@@ -11,12 +11,17 @@
#include "config.h"
#include "setup.h"
#include "device.h"
#include "iptvservice.h"
#if defined(APIVERSNUM) && APIVERSNUM < 10721
#error "VDR-1.7.21 API version or greater is required!"
#if defined(APIVERSNUM) && APIVERSNUM < 10730
#error "VDR-1.7.30 API version or greater is required!"
#endif
const char VERSION[] = "0.5.0";
#ifndef GITVERSION
#define GITVERSION ""
#endif
const char VERSION[] = "1.1.0" GITVERSION;
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
class cPluginIptv : public cPlugin {
@@ -94,7 +99,7 @@ bool cPluginIptv::Initialize(void)
{
debug("cPluginIptv::Initialize()\n");
// Initialize any background activities the plugin shall perform.
IptvConfig.SetConfigDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N));
IptvConfig.SetConfigDirectory(cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
return cIptvDevice::Initialize(deviceCount);
}
@@ -196,8 +201,18 @@ bool cPluginIptv::SetupParse(const char *Name, const char *Value)
bool cPluginIptv::Service(const char *Id, void *Data)
{
//debug("cPluginIptv::Service()\n");
// Handle custom service requests from other plugins
debug("cPluginIptv::Service()\n");
if (strcmp(Id,"IptvService-v1.0") == 0) {
if (Data) {
IptvService_v1_0 *data = (IptvService_v1_0*)Data;
cIptvDevice *dev = cIptvDevice::GetIptvDevice(data->cardIndex);
if (!dev)
return false;
data->protocol = dev->GetInformation(IPTV_DEVICE_INFO_PROTOCOL);
data->bitrate = dev->GetInformation(IPTV_DEVICE_INFO_BITRATE);
}
return true;
}
return false;
}

22
iptvservice.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* iptvservice.h: IPTV plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#ifndef __IPTVSERVICE_H
#define __IPTVSERVICE_H
#include <vdr/tools.h>
#define stIptv ('I' << 24)
struct IptvService_v1_0 {
unsigned int cardIndex;
cString protocol;
cString bitrate;
};
#endif //__IPTVSERVICE_H

View File

@@ -1,11 +0,0 @@
diff -Nru vdr-1.7.23-vanilla/pat.c vdr-1.7.23-disable_ca_updates/pat.c
--- vdr-1.7.23-vanilla/pat.c 2012-01-15 20:26:29.000000000 +0200
+++ vdr-1.7.23-disable_ca_updates/pat.c 2012-02-02 18:48:23.000000000 +0200
@@ -537,6 +537,7 @@
}
if (Setup.UpdateChannels >= 2) {
Channel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid);
+ if (!cSource::IsType(Channel->Source(), 'I'))
Channel->SetCaIds(CaDescriptors->CaIds());
Channel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds);
}

View File

@@ -5,10 +5,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 0.5.0\n"
"Project-Id-Version: vdr-iptv 1.0.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-02-02 02:02+0300\n"
"PO-Revision-Date: 2012-02-02 02:02+0300\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"Last-Translator: Tobias Grimm <tg@e-tobi.net>\n"
"Language-Team: German <vdr@linuxtv.org>\n"
"Language: de\n"

View File

@@ -5,10 +5,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 0.5.0\n"
"Project-Id-Version: vdr-iptv 1.0.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-02-02 02:02+0300\n"
"PO-Revision-Date: 2012-02-02 02:02+0300\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"Last-Translator: Rolf Ahrenberg\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n"
"Language: fi\n"

View File

@@ -6,10 +6,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 0.5.0\n"
"Project-Id-Version: vdr-iptv 1.0.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-02-02 02:02+0300\n"
"PO-Revision-Date: 2010-09-09 09:09+0300\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"Last-Translator: NIVAL Michaël <mnival@club-internet.fr>\n"
"Language-Team: French <vdr@linuxtv.org>\n"
"Language: fr\n"

View File

@@ -5,10 +5,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 0.5.0\n"
"Project-Id-Version: vdr-iptv 1.0.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-02-02 02:02+0300\n"
"PO-Revision-Date: 2012-02-02 02:02+0300\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n"
"Language: it\n"

View File

@@ -5,10 +5,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 0.5.0\n"
"Project-Id-Version: vdr-iptv 1.0.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-02-02 02:02+0300\n"
"PO-Revision-Date: 2012-02-02 02:02+0300\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"Last-Translator: Carel\n"
"Language-Team: Dutch <vdr@linuxtv.org>\n"
"Language: nl\n"

View File

@@ -5,10 +5,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: vdr-iptv 0.5.0\n"
"Project-Id-Version: vdr-iptv 1.0.0\n"
"Report-Msgid-Bugs-To: <see README>\n"
"POT-Creation-Date: 2012-02-02 02:02+0300\n"
"PO-Revision-Date: 2010-09-09 09:09+0300\n"
"POT-Creation-Date: 2012-06-03 06:03+0300\n"
"PO-Revision-Date: 2012-06-03 06:03+0300\n"
"Last-Translator: Alexander Gross <Bikalexander@gmail.com>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n"
"Language: ru\n"

View File

@@ -19,10 +19,15 @@
#include "config.h"
#include "protocolext.h"
#ifndef EXTSHELL
#define EXTSHELL "/bin/bash"
#endif
cIptvProtocolExt::cIptvProtocolExt()
: pid(-1),
scriptFile(""),
scriptParameter(0)
scriptParameter(0),
streamPort(0)
{
debug("cIptvProtocolExt::cIptvProtocolExt()\n");
}
@@ -53,9 +58,11 @@ void cIptvProtocolExt::ExecuteScript(void)
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
close(i);
// Execute the external script
cString cmd = cString::sprintf("%s %d %d", *scriptFile, scriptParameter, socketPort);
cString cmd = cString::sprintf("%s %d %d", *scriptFile, scriptParameter, streamPort);
debug("cIptvProtocolExt::ExecuteScript(child): %s\n", *cmd);
if (execl("/bin/bash", "sh", "-c", *cmd, (char *)NULL) == -1) {
// Create a new session for a process group
ERROR_IF_RET(setsid() == -1, "setsid()", _exit(-1));
if (execl(EXTSHELL, "sh", "-c", *cmd, (char *)NULL) == -1) {
error("Script execution failed: %s", *cmd);
_exit(-1);
}
@@ -74,27 +81,36 @@ void cIptvProtocolExt::TerminateScript(void)
if (pid > 0) {
const unsigned int timeoutms = 100;
unsigned int waitms = 0;
siginfo_t waitStatus;
bool waitOver = false;
// signal and wait for termination
int retval = kill(pid, SIGINT);
// Signal and wait for termination
int retval = killpg(pid, SIGINT);
ERROR_IF_RET(retval < 0, "kill()", waitOver = true);
while (!waitOver) {
retval = 0;
waitms += timeoutms;
if ((waitms % 2000) == 0) {
error("Script '%s' won't terminate - killing it!", *scriptFile);
kill(pid, SIGKILL);
killpg(pid, SIGKILL);
}
// Clear wait status to make sure child exit status is accessible
// and wait for child termination
#ifdef __FreeBSD__
int waitStatus = 0;
retval = waitpid(pid, &waitStatus, WNOHANG);
#else // __FreeBSD__
siginfo_t waitStatus;
memset(&waitStatus, '\0', sizeof(waitStatus));
// Wait for child termination
retval = waitid(P_PID, pid, &waitStatus, (WNOHANG | WEXITED));
#endif // __FreeBSD__
ERROR_IF_RET(retval < 0, "waitid()", waitOver = true);
// These are the acceptable conditions under which child exit is
// regarded as successful
#ifdef __FreeBSD__
if (retval > 0 && (WIFEXITED(waitStatus) || WIFSIGNALED(waitStatus))) {
#else // __FreeBSD__
if (!retval && waitStatus.si_pid && (waitStatus.si_pid == pid) &&
((waitStatus.si_code == CLD_EXITED) || (waitStatus.si_code == CLD_KILLED))) {
#endif // __FreeBSD__
debug("Child (%d) exited as expected\n", pid);
waitOver = true;
}
@@ -113,7 +129,7 @@ bool cIptvProtocolExt::Open(void)
if (!strlen(*scriptFile))
return false;
// Create the listening socket
OpenSocket(socketPort);
OpenSocket(streamPort);
// Execute the external script
ExecuteScript();
isActive = true;
@@ -149,7 +165,7 @@ bool cIptvProtocolExt::Set(const char* Location, const int Parameter, const int
}
scriptParameter = Parameter;
// Update listen port
socketPort = IptvConfig.GetExtProtocolBasePort() + Index;
streamPort = IptvConfig.GetExtProtocolBasePort() + Index;
}
return true;
}

View File

@@ -17,6 +17,7 @@ private:
int pid;
cString scriptFile;
int scriptParameter;
int streamPort;
private:
void TerminateScript(void);

View File

@@ -20,7 +20,8 @@
cIptvProtocolHttp::cIptvProtocolHttp()
: streamAddr(strdup("")),
streamPath(strdup("/"))
streamPath(strdup("/")),
streamPort(0)
{
debug("cIptvProtocolHttp::cIptvProtocolHttp()\n");
}
@@ -41,7 +42,7 @@ bool cIptvProtocolHttp::Connect(void)
// Check that stream address is valid
if (!isActive && !isempty(streamAddr) && !isempty(streamPath)) {
// Ensure that socket is valid and connect
OpenSocket(socketPort, streamAddr);
OpenSocket(streamPort, streamAddr);
if (!ConnectSocket()) {
CloseSocket();
return false;
@@ -127,15 +128,19 @@ bool cIptvProtocolHttp::ProcessHeaders(void)
{
debug("cIptvProtocolHttp::ProcessHeaders()\n");
unsigned int lineLength = 0;
int response = 0;
int version = 0, response = 0;
bool responseFound = false;
char fmt[32];
char buf[4096];
// Generate HTTP response format string with 2 arguments
snprintf(fmt, sizeof(fmt), "HTTP/1.%%%zui %%%zui ", sizeof(version) - 1, sizeof(response) - 1);
while (!responseFound || lineLength != 0) {
memset(buf, '\0', sizeof(buf));
if (!GetHeaderLine(buf, sizeof(buf), lineLength))
return false;
if (!responseFound && sscanf(buf, "HTTP/1.%*i %i ", &response) != 1) {
if (!responseFound && sscanf(buf, fmt, &version, &response) != 2) {
error("Expected HTTP header not found\n");
continue;
}
@@ -185,8 +190,8 @@ bool cIptvProtocolHttp::Set(const char* Location, const int Parameter, const int
}
else
streamPath = strcpyrealloc(streamPath, "/");
socketPort = Parameter;
//debug("http://%s:%d%s\n", streamAddr, socketPort, streamPath);
streamPort = Parameter;
//debug("http://%s:%d%s\n", streamAddr, streamPort, streamPath);
// Re-connect the socket
Connect();
}
@@ -196,5 +201,5 @@ bool cIptvProtocolHttp::Set(const char* Location, const int Parameter, const int
cString cIptvProtocolHttp::GetInformation(void)
{
//debug("cIptvProtocolHttp::GetInformation()");
return cString::sprintf("http://%s:%d%s", streamAddr, socketPort, streamPath);
return cString::sprintf("http://%s:%d%s", streamAddr, streamPort, streamPath);
}

View File

@@ -16,6 +16,7 @@ class cIptvProtocolHttp : public cIptvTcpSocket, public cIptvProtocolIf {
private:
char* streamAddr;
char* streamPath;
int streamPort;
private:
bool Connect(void);

View File

@@ -19,8 +19,10 @@
#include "socket.h"
cIptvProtocolUdp::cIptvProtocolUdp()
: streamAddr(strdup("")),
sourceAddr(strdup(""))
: isIGMPv3(false),
sourceAddr(strdup("")),
streamAddr(strdup("")),
streamPort(0)
{
debug("cIptvProtocolUdp::cIptvProtocolUdp()\n");
}
@@ -37,28 +39,29 @@ cIptvProtocolUdp::~cIptvProtocolUdp()
bool cIptvProtocolUdp::Open(void)
{
debug("cIptvProtocolUdp::Open()\n");
OpenSocket(socketPort, isempty(sourceAddr) ? INADDR_ANY : inet_addr(sourceAddr));
debug("cIptvProtocolUdp::Open(): streamAddr=%s\n", streamAddr);
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
if (!isempty(streamAddr)) {
// Join a new multicast group
JoinMulticast(inet_addr(streamAddr));
JoinMulticast();
}
return true;
}
bool cIptvProtocolUdp::Close(void)
{
debug("cIptvProtocolUdp::Close()\n");
debug("cIptvProtocolUdp::Close(): streamAddr=%s\n", streamAddr);
if (!isempty(streamAddr)) {
// Drop the multicast group
OpenSocket(socketPort, isempty(sourceAddr) ? INADDR_ANY : inet_addr(sourceAddr));
DropMulticast(inet_addr(streamAddr));
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
DropMulticast();
}
// Close the socket
CloseSocket();
// Reset stream and source addresses
streamAddr = strcpyrealloc(streamAddr, "");
sourceAddr = strcpyrealloc(sourceAddr, "");
// Do NOT reset stream and source addresses
//sourceAddr = strcpyrealloc(sourceAddr, "");
//streamAddr = strcpyrealloc(streamAddr, "");
//streamPort = 0;
return true;
}
@@ -73,23 +76,28 @@ bool cIptvProtocolUdp::Set(const char* Location, const int Parameter, const int
if (!isempty(Location)) {
// Drop the multicast group
if (!isempty(streamAddr)) {
OpenSocket(socketPort, isempty(sourceAddr) ? INADDR_ANY : inet_addr(sourceAddr));
DropMulticast(inet_addr(streamAddr));
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
DropMulticast();
}
// Update stream address and port
streamAddr = strcpyrealloc(streamAddr, Location);
char *p = strstr(streamAddr, ";");
// <group address> or <source address>@<group address>
char *p = strstr(streamAddr, "@");
if (p) {
sourceAddr = strcpyrealloc(sourceAddr, p + 1);
*p = 0;
sourceAddr = strcpyrealloc(sourceAddr, streamAddr);
streamAddr = strcpyrealloc(streamAddr, p + 1);
isIGMPv3 = true;
}
else
sourceAddr = strcpyrealloc(sourceAddr, "");
socketPort = Parameter;
else {
sourceAddr = strcpyrealloc(sourceAddr, streamAddr);
isIGMPv3 = false;
}
streamPort = Parameter;
// Join a new multicast group
if (!isempty(streamAddr)) {
OpenSocket(socketPort, isempty(sourceAddr) ? INADDR_ANY : inet_addr(sourceAddr));
JoinMulticast(inet_addr(streamAddr));
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
JoinMulticast();
}
}
return true;
@@ -98,5 +106,7 @@ bool cIptvProtocolUdp::Set(const char* Location, const int Parameter, const int
cString cIptvProtocolUdp::GetInformation(void)
{
//debug("cIptvProtocolUdp::GetInformation()");
return cString::sprintf("udp://%s:%d", streamAddr, socketPort);
if (isIGMPv3)
return cString::sprintf("udp://%s@%s:%d", sourceAddr, streamAddr, streamPort);
return cString::sprintf("udp://%s:%d", streamAddr, streamPort);
}

View File

@@ -14,8 +14,10 @@
class cIptvProtocolUdp : public cIptvUdpSocket, public cIptvProtocolIf {
private:
char* streamAddr;
bool isIGMPv3;
char* sourceAddr;
char* streamAddr;
int streamPort;
public:
cIptvProtocolUdp();

View File

@@ -5,10 +5,10 @@
*
*/
#include "config.h"
#include "sectionfilter.h"
cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, int Index,
uint16_t Pid, uint8_t Tid, uint8_t Mask)
cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, uint16_t Pid, uint8_t Tid, uint8_t Mask)
: pusi_seen(0),
feedcc(0),
doneq(0),
@@ -17,10 +17,9 @@ cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, int Index,
seclen(0),
tsfeedp(0),
pid(Pid),
devid(DeviceIndex),
id(Index)
devid(DeviceIndex)
{
//debug("cIptvSectionFilter::cIptvSectionFilter(%d, %d)\n", devid, id);
//debug("cIptvSectionFilter::cIptvSectionFilter(%d, %d)\n", devid, pid);
int i;
memset(secbuf_base, '\0', sizeof(secbuf_base));
@@ -47,35 +46,30 @@ cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, int Index,
}
doneq = local_doneq ? 1 : 0;
// Create sockets
socket[0] = socket[1] = -1;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socket) != 0) {
char tmp[64];
error("Opening section filter sockets failed (device=%d id=%d): %s\n", devid, id, strerror_r(errno, tmp, sizeof(tmp)));
}
else if ((fcntl(socket[0], F_SETFL, O_NONBLOCK) != 0) || (fcntl(socket[1], F_SETFL, O_NONBLOCK) != 0)) {
char tmp[64];
error("Setting section filter socket to non-blocking mode failed (device=%d id=%d): %s", devid, id, strerror_r(errno, tmp, sizeof(tmp)));
}
// Create filtering buffer
ringbuffer = new cRingBufferLinear(KILOBYTE(128), 0, false, *cString::sprintf("IPTV SECTION %d/%d", devid, pid));
if (ringbuffer)
ringbuffer->SetTimeouts(10, 10);
else
error("Failed to allocate buffer for section filter (device=%d pid=%d): ", devid, pid);
}
cIptvSectionFilter::~cIptvSectionFilter()
{
//debug("cIptvSectionFilter::~cIptvSectionfilter(%d, %d)\n", devid, id);
int tmp = socket[1];
socket[1] = -1;
if (tmp >= 0)
close(tmp);
tmp = socket[0];
socket[0] = -1;
if (tmp >= 0)
close(tmp);
//debug("cIptvSectionFilter::~cIptvSectionfilter(%d, %d)\n", devid, pid);
DELETE_POINTER(ringbuffer);
secbuf = NULL;
}
int cIptvSectionFilter::GetReadDesc(void)
int cIptvSectionFilter::Read(void *Data, size_t Length)
{
return socket[0];
int count = 0;
uchar *p = ringbuffer->Get(count);
if (p && count > 0) {
memcpy(Data, p, count);
ringbuffer->Del(count);
}
return count;
}
inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *Data)
@@ -105,10 +99,10 @@ int cIptvSectionFilter::Filter(void)
if (doneq && !neq)
return 0;
// There is no data in the read socket, more can be written
if ((socket[0] >= 0) && (socket[1] >= 0) /*&& !select_single_desc(socket[0], 0, false)*/) {
ssize_t len = write(socket[1], secbuf, seclen);
ERROR_IF(len < 0, "write()");
if (ringbuffer) {
int len = ringbuffer->Put(secbuf, seclen);
if (len != seclen)
ringbuffer->ReportOverflow(seclen - len);
// Update statistics
AddSectionStatistic(len, 1);
}
@@ -138,7 +132,7 @@ int cIptvSectionFilter::CopyDump(const uint8_t *buf, uint8_t len)
return 0;
memcpy(secbuf_base + tsfeedp, buf, len);
tsfeedp += len;
tsfeedp = uint16_t(tsfeedp + len);
limit = tsfeedp;
if (limit > DMX_MAX_SECFEED_SIZE)
@@ -154,7 +148,7 @@ int cIptvSectionFilter::CopyDump(const uint8_t *buf, uint8_t len)
seclen = seclen_local;
if (pusi_seen)
Feed();
secbufp += seclen_local;
secbufp = uint16_t(secbufp + seclen_local);
secbuf += seclen_local;
}
return 0;

View File

@@ -8,6 +8,9 @@
#ifndef __IPTV_SECTIONFILTER_H
#define __IPTV_SECTIONFILTER_H
#ifdef __FreeBSD__
#include <sys/socket.h>
#endif // __FreeBSD__
#include <vdr/device.h>
#include "common.h"
@@ -33,8 +36,6 @@ private:
uint16_t pid;
int devid;
int id;
int socket[2];
uint8_t filter_value[DMX_MAX_FILTER_SIZE];
uint8_t filter_mask[DMX_MAX_FILTER_SIZE];
@@ -43,6 +44,8 @@ private:
uint8_t maskandmode[DMX_MAX_FILTER_SIZE];
uint8_t maskandnotmode[DMX_MAX_FILTER_SIZE];
cRingBufferLinear *ringbuffer;
inline uint16_t GetLength(const uint8_t *Data);
void New(void);
int Filter(void);
@@ -51,11 +54,10 @@ private:
public:
// constructor & destructor
cIptvSectionFilter(int Index, int DeviceIndex, uint16_t Pid,
uint8_t Tid, uint8_t Mask);
cIptvSectionFilter(int DeviceIndex, uint16_t Pid, uint8_t Tid, uint8_t Mask);
virtual ~cIptvSectionFilter();
void Process(const uint8_t* Data);
int GetReadDesc(void);
int Read(void *Buffer, size_t Length);
uint16_t GetPid(void) const { return pid; }
};

265
socket.c
View File

@@ -7,6 +7,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
@@ -18,8 +19,8 @@
#include "socket.h"
cIptvSocket::cIptvSocket()
: socketDesc(-1),
socketPort(0),
: socketPort(0),
socketDesc(-1),
isActive(false)
{
debug("cIptvSocket::cIptvSocket()\n");
@@ -57,11 +58,13 @@ bool cIptvSocket::OpenSocket(const int Port, const bool isUdp)
// Allow multiple sockets to use the same PORT number
ERROR_IF_FUNC(setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0, "setsockopt(SO_REUSEADDR)",
CloseSocket(), return false);
#ifndef __FreeBSD__
// Allow packet information to be fetched
ERROR_IF_FUNC(setsockopt(socketDesc, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0, "setsockopt(IP_PKTINFO)",
CloseSocket(), return false);
#endif // __FreeBSD__
// Bind socket
memset(&sockAddr, '\0', sizeof(sockAddr));
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons((uint16_t)(Port & 0xFFFF));
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -87,9 +90,33 @@ void cIptvSocket::CloseSocket(void)
}
}
bool cIptvSocket::CheckAddress(const char *Addr, in_addr_t *InAddr)
{
if (InAddr) {
// First try only the IP address
*InAddr = htonl(inet_addr(Addr));
if (*InAddr == htonl(INADDR_NONE)) {
debug("Cannot convert %s directly to internet address\n", Addr);
// It may be a host name, get the name
struct hostent *host;
host = gethostbyname(Addr);
if (!host) {
char tmp[64];
error("gethostbyname() failed: %s is not valid address: %s", Addr, strerror_r(h_errno, tmp, sizeof(tmp)));
return false;
}
*InAddr = htonl(inet_addr(*host->h_addr_list));
}
return true;
}
return false;
}
// UDP socket class
cIptvUdpSocket::cIptvUdpSocket()
: sourceAddr(INADDR_ANY)
: streamAddr(htonl(INADDR_ANY)),
sourceAddr(htonl(INADDR_ANY)),
useIGMPv3(false)
{
debug("cIptvUdpSocket::cIptvUdpSocket()\n");
}
@@ -99,46 +126,95 @@ cIptvUdpSocket::~cIptvUdpSocket()
debug("cIptvUdpSocket::~cIptvUdpSocket()\n");
}
bool cIptvUdpSocket::OpenSocket(const int Port, const in_addr_t SourceAddr)
bool cIptvUdpSocket::OpenSocket(const int Port)
{
debug("cIptvUdpSocket::OpenSocket()\n");
sourceAddr = SourceAddr;
streamAddr = htonl(INADDR_ANY);
sourceAddr = htonl(INADDR_ANY);
useIGMPv3 = false;
return cIptvSocket::OpenSocket(Port, true);
}
bool cIptvUdpSocket::OpenSocket(const int Port, const char *StreamAddr, const char *SourceAddr, bool UseIGMPv3)
{
debug("cIptvUdpSocket::OpenSocket()\n");
CheckAddress(StreamAddr, &streamAddr);
CheckAddress(SourceAddr, &sourceAddr);
useIGMPv3 = UseIGMPv3;
return cIptvSocket::OpenSocket(Port, true);
}
void cIptvUdpSocket::CloseSocket(void)
{
debug("cIptvUdpSocket::CloseSocket()\n");
sourceAddr = INADDR_ANY;
streamAddr = htonl(INADDR_ANY);
sourceAddr = htonl(INADDR_ANY);
useIGMPv3 = false;
cIptvSocket::CloseSocket();
}
bool cIptvUdpSocket::JoinMulticast(const in_addr_t StreamAddr)
bool cIptvUdpSocket::JoinMulticast(void)
{
debug("cIptvUdpSocket::JoinMulticast()\n");
// Check if socket exists
if (!isActive && (socketDesc >= 0)) {
// Join a new multicast group
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = StreamAddr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
ERROR_IF_RET(setsockopt(socketDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_ADD_MEMBERSHIP)", return false);
if (useIGMPv3) {
// Source-specific multicast (SSM) is used
struct group_source_req gsr;
struct sockaddr_in *grp;
struct sockaddr_in *src;
gsr.gsr_interface = 0; // if_nametoindex("any") ?
grp = (struct sockaddr_in*)&gsr.gsr_group;
grp->sin_family = AF_INET;
grp->sin_addr.s_addr = streamAddr;
grp->sin_port = 0;
src = (struct sockaddr_in*)&gsr.gsr_source;
src->sin_family = AF_INET;
src->sin_addr.s_addr = sourceAddr;
src->sin_port = 0;
ERROR_IF_RET(setsockopt(socketDesc, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &gsr, sizeof(gsr)) < 0, "setsockopt(MCAST_JOIN_SOURCE_GROUP)", return false);
}
else {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = streamAddr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
ERROR_IF_RET(setsockopt(socketDesc, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_ADD_MEMBERSHIP)", return false);
}
// Update multicasting flag
isActive = true;
}
return true;
}
bool cIptvUdpSocket::DropMulticast(const in_addr_t StreamAddr)
bool cIptvUdpSocket::DropMulticast(void)
{
debug("cIptvUdpSocket::DropMulticast()\n");
// Check if socket exists
if (isActive && (socketDesc >= 0)) {
// Drop the existing multicast group
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = StreamAddr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
ERROR_IF_RET(setsockopt(socketDesc, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_DROP_MEMBERSHIP)", return false);
if (useIGMPv3) {
// Source-specific multicast (SSM) is used
struct group_source_req gsr;
struct sockaddr_in *grp;
struct sockaddr_in *src;
gsr.gsr_interface = 0; // if_nametoindex("any") ?
grp = (struct sockaddr_in*)&gsr.gsr_group;
grp->sin_family = AF_INET;
grp->sin_addr.s_addr = streamAddr;
grp->sin_port = 0;
src = (struct sockaddr_in*)&gsr.gsr_source;
src->sin_family = AF_INET;
src->sin_addr.s_addr = sourceAddr;
src->sin_port = 0;
ERROR_IF_RET(setsockopt(socketDesc, SOL_IP, MCAST_LEAVE_SOURCE_GROUP, &gsr, sizeof(gsr)) < 0, "setsockopt(MCAST_LEAVE_SOURCE_GROUP)", return false);
}
else {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = streamAddr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
ERROR_IF_RET(setsockopt(socketDesc, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_DROP_MEMBERSHIP)", return false);
}
// Update multicasting flag
isActive = false;
}
@@ -154,72 +230,77 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
error("Invalid socket in %s\n", __FUNCTION__);
return -1;
}
socklen_t addrlen = sizeof(sockAddr);
int len = 0;
struct msghdr msgh;
struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[256];
// Initialize iov and msgh structures
memset(&msgh, 0, sizeof(struct msghdr));
iov.iov_base = BufferAddr;
iov.iov_len = BufferLen;
msgh.msg_control = cbuf;
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = &sockAddr;
msgh.msg_namelen = addrlen;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_flags = 0;
// Read data from socket
if (isActive && socketDesc && BufferAddr && (BufferLen > 0))
len = (int)recvmsg(socketDesc, &msgh, MSG_DONTWAIT);
if (len < 0) {
ERROR_IF(errno != EAGAIN, "recvmsg()");
return -1;
}
else if (len > 0) {
// Process auxiliary received data and validate source address
for (cmsg = CMSG_FIRSTHDR(&msgh); (sourceAddr != INADDR_ANY) && (cmsg != NULL); cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg);
if (i->ipi_addr.s_addr != sourceAddr) {
//debug("Discard packet due to invalid source address: %s", inet_ntoa(i->ipi_addr));
return 0;
}
}
}
if (BufferAddr[0] == TS_SYNC_BYTE)
return len;
else if (len > 3) {
// http://www.networksorcery.com/enp/rfc/rfc2250.txt
// version
unsigned int v = (BufferAddr[0] >> 6) & 0x03;
// extension bit
unsigned int x = (BufferAddr[0] >> 4) & 0x01;
// cscr count
unsigned int cc = BufferAddr[0] & 0x0F;
// payload type: MPEG2 TS = 33
//unsigned int pt = readBuffer[1] & 0x7F;
// header lenght
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
// check if extension
if (x) {
// extension header length
unsigned int ehl = (((BufferAddr[headerlen + 2] & 0xFF) << 8) |
(BufferAddr[headerlen + 3] & 0xFF));
// update header length
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
// Read data from socket in a loop
do {
socklen_t addrlen = sizeof(sockAddr);
struct msghdr msgh;
struct iovec iov;
char cbuf[256];
len = 0;
// Initialize iov and msgh structures
memset(&msgh, 0, sizeof(struct msghdr));
iov.iov_base = BufferAddr;
iov.iov_len = BufferLen;
msgh.msg_control = cbuf;
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = &sockAddr;
msgh.msg_namelen = addrlen;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
msgh.msg_flags = 0;
if (isActive && socketDesc && BufferAddr && (BufferLen > 0))
len = (int)recvmsg(socketDesc, &msgh, MSG_DONTWAIT);
else
break;
if (len > 0) {
#ifndef __FreeBSD__
// Process auxiliary received data and validate source address
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg);
if ((i->ipi_addr.s_addr == streamAddr) || (htonl(INADDR_ANY) == streamAddr)) {
#endif // __FreeBSD__
if (BufferAddr[0] == TS_SYNC_BYTE)
return len;
else if (len > 3) {
// http://www.networksorcery.com/enp/rfc/rfc2250.txt
// version
unsigned int v = (BufferAddr[0] >> 6) & 0x03;
// extension bit
unsigned int x = (BufferAddr[0] >> 4) & 0x01;
// cscr count
unsigned int cc = BufferAddr[0] & 0x0F;
// payload type: MPEG2 TS = 33
//unsigned int pt = readBuffer[1] & 0x7F;
// header lenght
unsigned int headerlen = (3 + cc) * (unsigned int)sizeof(uint32_t);
// check if extension
if (x) {
// extension header length
unsigned int ehl = (((BufferAddr[headerlen + 2] & 0xFF) << 8) |
(BufferAddr[headerlen + 3] & 0xFF));
// update header length
headerlen += (ehl + 1) * (unsigned int)sizeof(uint32_t);
}
// Check that rtp is version 2 and payload contains multiple of TS packet data
if ((v == 2) && (((len - headerlen) % TS_SIZE) == 0) &&
(BufferAddr[headerlen] == TS_SYNC_BYTE)) {
// Set argument point to payload in read buffer
memmove(BufferAddr, &BufferAddr[headerlen], (len - headerlen));
return (len - headerlen);
}
}
#ifndef __FreeBSD__
}
}
}
// Check that rtp is version 2 and payload contains multiple of TS packet data
if ((v == 2) && (((len - headerlen) % TS_SIZE) == 0) &&
(BufferAddr[headerlen] == TS_SYNC_BYTE)) {
// Set argument point to payload in read buffer
memmove(BufferAddr, &BufferAddr[headerlen], (len - headerlen));
return (len - headerlen);
}
}
}
#endif // __FreeBSD__
}
} while (len > 0);
ERROR_IF_RET(len < 0 && errno != EAGAIN, "recvmsg()", return -1);
return 0;
}
@@ -237,26 +318,8 @@ cIptvTcpSocket::~cIptvTcpSocket()
bool cIptvTcpSocket::OpenSocket(const int Port, const char *StreamAddr)
{
debug("cIptvTcpSocket::OpenSocket()\n");
// First try only the IP address
sockAddr.sin_addr.s_addr = inet_addr(StreamAddr);
if (sockAddr.sin_addr.s_addr == INADDR_NONE) {
debug("Cannot convert %s directly to internet address\n", StreamAddr);
// It may be a host name, get the name
struct hostent *host;
host = gethostbyname(StreamAddr);
if (!host) {
char tmp[64];
error("gethostbyname() failed: %s is not valid address: %s", StreamAddr, strerror_r(h_errno, tmp, sizeof(tmp)));
return false;
}
sockAddr.sin_addr.s_addr = inet_addr(*host->h_addr_list);
}
return cIptvSocket::OpenSocket(Port, false);
// Socket must be opened before setting the host address
return (cIptvSocket::OpenSocket(Port, false) && CheckAddress(StreamAddr, &sockAddr.sin_addr.s_addr));
}
void cIptvTcpSocket::CloseSocket(void)

View File

@@ -9,17 +9,23 @@
#define __IPTV_SOCKET_H
#include <arpa/inet.h>
#ifdef __FreeBSD__
#include <netinet/in.h>
#endif // __FreeBSD__
class cIptvSocket {
private:
int socketPort;
protected:
int socketDesc;
int socketPort;
struct sockaddr_in sockAddr;
bool isActive;
protected:
bool OpenSocket(const int Port, const bool isUdp);
void CloseSocket(void);
bool CheckAddress(const char *Addr, in_addr_t *InAddr);
public:
cIptvSocket();
@@ -28,16 +34,19 @@ public:
class cIptvUdpSocket : public cIptvSocket {
private:
in_addr_t streamAddr;
in_addr_t sourceAddr;
bool useIGMPv3;
public:
cIptvUdpSocket();
virtual ~cIptvUdpSocket();
virtual int Read(unsigned char* BufferAddr, unsigned int BufferLen);
bool OpenSocket(const int Port, const in_addr_t SourceAddr = INADDR_ANY);
bool OpenSocket(const int Port);
bool OpenSocket(const int Port, const char *StreamAddr, const char *SourceAddr, bool UseIGMPv3);
void CloseSocket(void);
bool JoinMulticast(const in_addr_t StreamAddr);
bool DropMulticast(const in_addr_t StreamAddr);
bool JoinMulticast(void);
bool DropMulticast(void);
};
class cIptvTcpSocket : public cIptvSocket {

View File

@@ -148,7 +148,7 @@ cString cIptvStreamerStatistics::GetStreamerStatistic()
long bitrate = elapsed ? (long)(1000.0L * dataBytes / KILOBYTE(1) / elapsed) : 0L;
if (!IptvConfig.GetUseBytes())
bitrate *= 8;
cString info = cString::sprintf("Stream bitrate: %ld k%s/s\n", bitrate, IptvConfig.GetUseBytes() ? "B" : "bit");
cString info = cString::sprintf("%ld k%s/s", bitrate, IptvConfig.GetUseBytes() ? "B" : "bit");
dataBytes = 0;
return info;
}

View File

@@ -108,8 +108,8 @@ bool cIptvStreamer::Set(const char* Location, const int Parameter, const int Ind
cString cIptvStreamer::GetInformation(void)
{
//debug("cIptvStreamer::GetInformation()");
cString info("Stream:");
cString info;
if (protocol)
info = cString::sprintf("%s %s", *info, *protocol->GetInformation());
return cString::sprintf("%s\n", *info);
info = protocol->GetInformation();
return info;
}