mirror of
https://github.com/rofafor/vdr-plugin-iptv.git
synced 2023-10-10 13:37:03 +02:00
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.
This commit is contained in:
parent
75d17b289b
commit
11d864c16b
7
HISTORY
7
HISTORY
@ -181,3 +181,10 @@ VDR Plugin 'iptv' Revision History
|
|||||||
2012-07-10: Version 1.0.1
|
2012-07-10: Version 1.0.1
|
||||||
|
|
||||||
- Added FreeBSD support (Thanks to Jürgen Lock).
|
- 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.
|
||||||
|
18
README
18
README
@ -44,7 +44,7 @@ cd /put/your/path/here/VDR/PLUGINS/src
|
|||||||
tar -xzf /put/your/path/here/vdr-iptv-X.Y.Z.tgz
|
tar -xzf /put/your/path/here/vdr-iptv-X.Y.Z.tgz
|
||||||
ln -s iptv-X.Y.Z iptv
|
ln -s iptv-X.Y.Z iptv
|
||||||
cd /put/your/path/here/VDR
|
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
|
||||||
make plugins
|
make plugins
|
||||||
./vdr -P iptv
|
./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
|
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
|
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
|
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
|
TV1;IPTV:10:S=1|P=0|F=UDP|U=127.0.0.1|A=1234:I:0:512:650:2321:0:1:0:0:0
|
||||||
^ ^ ^ ^ ^ ^ ^
|
^ ^ ^ ^ ^ ^ ^
|
||||||
| | | | | | Source type ("I")
|
| | | | | | Source type ("I")
|
||||||
| | | | | Stream parameter (multicast port
|
| | | | | Stream parameter (multicast port
|
||||||
| | | | | number, HTTP port number, file delay
|
| | | | | number, HTTP port number, file delay
|
||||||
| | | | | (ms), script parameter)
|
| | | | | (ms), script parameter)
|
||||||
| | | | Stream address (multicast address, URL, file
|
| | | | Stream address (multicast source@group address,
|
||||||
| | | | location, script location)
|
| | | | URL, file location, script location)
|
||||||
| | | Stream protocol ("UDP", "HTTP", "FILE", "EXT")
|
| | | Stream protocol ("UDP", "HTTP", "FILE", "EXT")
|
||||||
| | Pid scanner ("0" disable, "1" enable)
|
| | Pid scanner ("0" disable, "1" enable)
|
||||||
| Section id (Sid/Nid/Tid) scanner ("0" disable, "1" enable)
|
| Section id (Sid/Nid/Tid) scanner ("0" disable, "1" enable)
|
||||||
@ -119,9 +120,9 @@ External streaming:
|
|||||||
|
|
||||||
- To watch an externally received channel add an EXT entry to channels.conf
|
- 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
|
and specify a script name and parameter. The specified script is executed
|
||||||
from plugin configuration directory when VDR tunes to the channel. The
|
from plugin resource directory when VDR tunes to the channel. The specified
|
||||||
specified script parameter is passed to the script and it can be used to
|
script parameter is passed to the script and it can be used to select for
|
||||||
select for example between different URLs.
|
example between different URLs.
|
||||||
|
|
||||||
- When an EXT channel is opened the IPTV plugin opens an UDP listening port
|
- 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
|
on the localhost. The external script is responsible for supplying IPTV
|
||||||
@ -167,6 +168,11 @@ Notes:
|
|||||||
- Section id and pid scanners should be disabled after the correct data is
|
- Section id and pid scanners should be disabled after the correct data is
|
||||||
found. This can be made via VDR's channel editor.
|
found. This can be made via VDR's channel editor.
|
||||||
|
|
||||||
|
- 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:
|
Acknowledgements:
|
||||||
|
|
||||||
- The IPTV section filtering code is derived from Linux kernel.
|
- The IPTV section filtering code is derived from Linux kernel.
|
||||||
|
37
device.c
37
device.c
@ -343,26 +343,37 @@ int cIptvDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
|
|||||||
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
||||||
if (!secfilters[i]) {
|
if (!secfilters[i]) {
|
||||||
//debug("cIptvDevice::OpenFilter(%d): Pid=%d Tid=%02X Mask=%02X Index=%d\n", deviceIndex, Pid, Tid, Mask, 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);
|
secfilters[i] = new cIptvSectionFilter(deviceIndex, Pid, Tid, Mask);
|
||||||
return secfilters[i]->GetReadDesc();
|
if (secfilters[i])
|
||||||
|
return i;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No free filter slot found
|
// No free filter slot found
|
||||||
return -1;
|
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)
|
void cIptvDevice::CloseFilter(int Handle)
|
||||||
{
|
{
|
||||||
// Lock
|
// Lock
|
||||||
cMutexLock MutexLock(&mutex);
|
cMutexLock MutexLock(&mutex);
|
||||||
// Search the filter for deletion
|
// ... and load
|
||||||
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
|
if (secfilters[Handle]) {
|
||||||
if (secfilters[i] && (Handle == secfilters[i]->GetReadDesc())) {
|
//debug("cIptvDevice::CloseFilter(%d): %d\n", deviceIndex, Handle);
|
||||||
//debug("cIptvDevice::CloseFilter(%d): %d\n", deviceIndex, Handle);
|
DeleteFilter(Handle);
|
||||||
DeleteFilter(i);
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvDevice::OpenDvr(void)
|
bool cIptvDevice::OpenDvr(void)
|
||||||
@ -395,6 +406,12 @@ bool cIptvDevice::HasLock(int TimeoutMs)
|
|||||||
return (!IsBuffering());
|
return (!IsBuffering());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cIptvDevice::HasInternalCam(void)
|
||||||
|
{
|
||||||
|
//debug("cIptvDevice::HasInternalCam(%d)\n", deviceIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void cIptvDevice::ResetBuffering(void)
|
void cIptvDevice::ResetBuffering(void)
|
||||||
{
|
{
|
||||||
debug("cIptvDevice::ResetBuffering(%d)\n", deviceIndex);
|
debug("cIptvDevice::ResetBuffering(%d)\n", deviceIndex);
|
||||||
|
6
device.h
6
device.h
@ -76,6 +76,7 @@ private:
|
|||||||
bool IsBlackListed(u_short Pid, u_char Tid, u_char Mask) const;
|
bool IsBlackListed(u_short Pid, u_char Tid, u_char Mask) const;
|
||||||
|
|
||||||
// for channel info
|
// for channel info
|
||||||
|
public:
|
||||||
virtual cString DeviceType(void) const;
|
virtual cString DeviceType(void) const;
|
||||||
virtual cString DeviceName(void) const;
|
virtual cString DeviceName(void) const;
|
||||||
virtual int SignalStrength(void) const;
|
virtual int SignalStrength(void) const;
|
||||||
@ -101,11 +102,16 @@ protected:
|
|||||||
// for section filtering
|
// for section filtering
|
||||||
public:
|
public:
|
||||||
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
|
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);
|
virtual void CloseFilter(int Handle);
|
||||||
|
|
||||||
// for transponder lock
|
// for transponder lock
|
||||||
public:
|
public:
|
||||||
virtual bool HasLock(int);
|
virtual bool HasLock(int);
|
||||||
|
|
||||||
|
// for common interface
|
||||||
|
public:
|
||||||
|
virtual bool HasInternalCam(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __IPTV_DEVICE_H
|
#endif // __IPTV_DEVICE_H
|
||||||
|
8
iptv.c
8
iptv.c
@ -13,15 +13,15 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "iptvservice.h"
|
#include "iptvservice.h"
|
||||||
|
|
||||||
#if defined(APIVERSNUM) && APIVERSNUM < 10727
|
#if defined(APIVERSNUM) && APIVERSNUM < 10730
|
||||||
#error "VDR-1.7.27 API version or greater is required!"
|
#error "VDR-1.7.30 API version or greater is required!"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GITVERSION
|
#ifndef GITVERSION
|
||||||
#define GITVERSION ""
|
#define GITVERSION ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char VERSION[] = "1.0.1" GITVERSION;
|
const char VERSION[] = "1.1.0" GITVERSION;
|
||||||
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
||||||
|
|
||||||
class cPluginIptv : public cPlugin {
|
class cPluginIptv : public cPlugin {
|
||||||
@ -99,7 +99,7 @@ bool cPluginIptv::Initialize(void)
|
|||||||
{
|
{
|
||||||
debug("cPluginIptv::Initialize()\n");
|
debug("cPluginIptv::Initialize()\n");
|
||||||
// Initialize any background activities the plugin shall perform.
|
// 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);
|
return cIptvDevice::Initialize(deviceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@
|
|||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
cIptvProtocolUdp::cIptvProtocolUdp()
|
cIptvProtocolUdp::cIptvProtocolUdp()
|
||||||
: streamAddr(strdup("")),
|
: isIGMPv3(false),
|
||||||
|
sourceAddr(strdup("")),
|
||||||
|
streamAddr(strdup("")),
|
||||||
streamPort(0)
|
streamPort(0)
|
||||||
{
|
{
|
||||||
debug("cIptvProtocolUdp::cIptvProtocolUdp()\n");
|
debug("cIptvProtocolUdp::cIptvProtocolUdp()\n");
|
||||||
@ -32,12 +34,13 @@ cIptvProtocolUdp::~cIptvProtocolUdp()
|
|||||||
cIptvProtocolUdp::Close();
|
cIptvProtocolUdp::Close();
|
||||||
// Free allocated memory
|
// Free allocated memory
|
||||||
free(streamAddr);
|
free(streamAddr);
|
||||||
|
free(sourceAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvProtocolUdp::Open(void)
|
bool cIptvProtocolUdp::Open(void)
|
||||||
{
|
{
|
||||||
debug("cIptvProtocolUdp::Open(): streamAddr=%s\n", streamAddr);
|
debug("cIptvProtocolUdp::Open(): streamAddr=%s\n", streamAddr);
|
||||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||||
if (!isempty(streamAddr)) {
|
if (!isempty(streamAddr)) {
|
||||||
// Join a new multicast group
|
// Join a new multicast group
|
||||||
JoinMulticast();
|
JoinMulticast();
|
||||||
@ -50,12 +53,13 @@ bool cIptvProtocolUdp::Close(void)
|
|||||||
debug("cIptvProtocolUdp::Close(): streamAddr=%s\n", streamAddr);
|
debug("cIptvProtocolUdp::Close(): streamAddr=%s\n", streamAddr);
|
||||||
if (!isempty(streamAddr)) {
|
if (!isempty(streamAddr)) {
|
||||||
// Drop the multicast group
|
// Drop the multicast group
|
||||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||||
DropMulticast();
|
DropMulticast();
|
||||||
}
|
}
|
||||||
// Close the socket
|
// Close the socket
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
// Do NOT reset stream and source addresses
|
// Do NOT reset stream and source addresses
|
||||||
|
//sourceAddr = strcpyrealloc(sourceAddr, "");
|
||||||
//streamAddr = strcpyrealloc(streamAddr, "");
|
//streamAddr = strcpyrealloc(streamAddr, "");
|
||||||
//streamPort = 0;
|
//streamPort = 0;
|
||||||
return true;
|
return true;
|
||||||
@ -72,15 +76,27 @@ bool cIptvProtocolUdp::Set(const char* Location, const int Parameter, const int
|
|||||||
if (!isempty(Location)) {
|
if (!isempty(Location)) {
|
||||||
// Drop the multicast group
|
// Drop the multicast group
|
||||||
if (!isempty(streamAddr)) {
|
if (!isempty(streamAddr)) {
|
||||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||||
DropMulticast();
|
DropMulticast();
|
||||||
}
|
}
|
||||||
// Update stream address and port
|
// Update stream address and port
|
||||||
streamAddr = strcpyrealloc(streamAddr, Location);
|
streamAddr = strcpyrealloc(streamAddr, Location);
|
||||||
|
// <group address> or <source address>@<group address>
|
||||||
|
char *p = strstr(streamAddr, "@");
|
||||||
|
if (p) {
|
||||||
|
*p = 0;
|
||||||
|
sourceAddr = strcpyrealloc(sourceAddr, streamAddr);
|
||||||
|
streamAddr = strcpyrealloc(streamAddr, p + 1);
|
||||||
|
isIGMPv3 = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sourceAddr = strcpyrealloc(sourceAddr, streamAddr);
|
||||||
|
isIGMPv3 = false;
|
||||||
|
}
|
||||||
streamPort = Parameter;
|
streamPort = Parameter;
|
||||||
// Join a new multicast group
|
// Join a new multicast group
|
||||||
if (!isempty(streamAddr)) {
|
if (!isempty(streamAddr)) {
|
||||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||||
JoinMulticast();
|
JoinMulticast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,5 +106,7 @@ bool cIptvProtocolUdp::Set(const char* Location, const int Parameter, const int
|
|||||||
cString cIptvProtocolUdp::GetInformation(void)
|
cString cIptvProtocolUdp::GetInformation(void)
|
||||||
{
|
{
|
||||||
//debug("cIptvProtocolUdp::GetInformation()");
|
//debug("cIptvProtocolUdp::GetInformation()");
|
||||||
|
if (isIGMPv3)
|
||||||
|
return cString::sprintf("udp://%s@%s:%d", sourceAddr, streamAddr, streamPort);
|
||||||
return cString::sprintf("udp://%s:%d", streamAddr, streamPort);
|
return cString::sprintf("udp://%s:%d", streamAddr, streamPort);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
class cIptvProtocolUdp : public cIptvUdpSocket, public cIptvProtocolIf {
|
class cIptvProtocolUdp : public cIptvUdpSocket, public cIptvProtocolIf {
|
||||||
private:
|
private:
|
||||||
|
bool isIGMPv3;
|
||||||
|
char* sourceAddr;
|
||||||
char* streamAddr;
|
char* streamAddr;
|
||||||
int streamPort;
|
int streamPort;
|
||||||
|
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "sectionfilter.h"
|
#include "sectionfilter.h"
|
||||||
|
|
||||||
cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, int Index,
|
cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, uint16_t Pid, uint8_t Tid, uint8_t Mask)
|
||||||
uint16_t Pid, uint8_t Tid, uint8_t Mask)
|
|
||||||
: pusi_seen(0),
|
: pusi_seen(0),
|
||||||
feedcc(0),
|
feedcc(0),
|
||||||
doneq(0),
|
doneq(0),
|
||||||
@ -17,10 +17,9 @@ cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, int Index,
|
|||||||
seclen(0),
|
seclen(0),
|
||||||
tsfeedp(0),
|
tsfeedp(0),
|
||||||
pid(Pid),
|
pid(Pid),
|
||||||
devid(DeviceIndex),
|
devid(DeviceIndex)
|
||||||
id(Index)
|
|
||||||
{
|
{
|
||||||
//debug("cIptvSectionFilter::cIptvSectionFilter(%d, %d)\n", devid, id);
|
//debug("cIptvSectionFilter::cIptvSectionFilter(%d, %d)\n", devid, pid);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
memset(secbuf_base, '\0', sizeof(secbuf_base));
|
memset(secbuf_base, '\0', sizeof(secbuf_base));
|
||||||
@ -47,35 +46,30 @@ cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, int Index,
|
|||||||
}
|
}
|
||||||
doneq = local_doneq ? 1 : 0;
|
doneq = local_doneq ? 1 : 0;
|
||||||
|
|
||||||
// Create sockets
|
// Create filtering buffer
|
||||||
socket[0] = socket[1] = -1;
|
ringbuffer = new cRingBufferLinear(KILOBYTE(128), 0, false, *cString::sprintf("IPTV SECTION %d/%d", devid, pid));
|
||||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socket) != 0) {
|
if (ringbuffer)
|
||||||
char tmp[64];
|
ringbuffer->SetTimeouts(10, 10);
|
||||||
error("Opening section filter sockets failed (device=%d id=%d): %s\n", devid, id, strerror_r(errno, tmp, sizeof(tmp)));
|
else
|
||||||
}
|
error("Failed to allocate buffer for section filter (device=%d pid=%d): ", devid, pid);
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cIptvSectionFilter::~cIptvSectionFilter()
|
cIptvSectionFilter::~cIptvSectionFilter()
|
||||||
{
|
{
|
||||||
//debug("cIptvSectionFilter::~cIptvSectionfilter(%d, %d)\n", devid, id);
|
//debug("cIptvSectionFilter::~cIptvSectionfilter(%d, %d)\n", devid, pid);
|
||||||
int tmp = socket[1];
|
DELETE_POINTER(ringbuffer);
|
||||||
socket[1] = -1;
|
|
||||||
if (tmp >= 0)
|
|
||||||
close(tmp);
|
|
||||||
tmp = socket[0];
|
|
||||||
socket[0] = -1;
|
|
||||||
if (tmp >= 0)
|
|
||||||
close(tmp);
|
|
||||||
secbuf = NULL;
|
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)
|
inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *Data)
|
||||||
@ -105,10 +99,10 @@ int cIptvSectionFilter::Filter(void)
|
|||||||
if (doneq && !neq)
|
if (doneq && !neq)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// There is no data in the read socket, more can be written
|
if (ringbuffer) {
|
||||||
if ((socket[0] >= 0) && (socket[1] >= 0) /*&& !select_single_desc(socket[0], 0, false)*/) {
|
int len = ringbuffer->Put(secbuf, seclen);
|
||||||
ssize_t len = write(socket[1], secbuf, seclen);
|
if (len != seclen)
|
||||||
ERROR_IF(len < 0, "write()");
|
ringbuffer->ReportOverflow(seclen - len);
|
||||||
// Update statistics
|
// Update statistics
|
||||||
AddSectionStatistic(len, 1);
|
AddSectionStatistic(len, 1);
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,6 @@ private:
|
|||||||
uint16_t pid;
|
uint16_t pid;
|
||||||
|
|
||||||
int devid;
|
int devid;
|
||||||
int id;
|
|
||||||
int socket[2];
|
|
||||||
|
|
||||||
uint8_t filter_value[DMX_MAX_FILTER_SIZE];
|
uint8_t filter_value[DMX_MAX_FILTER_SIZE];
|
||||||
uint8_t filter_mask[DMX_MAX_FILTER_SIZE];
|
uint8_t filter_mask[DMX_MAX_FILTER_SIZE];
|
||||||
@ -46,6 +44,8 @@ private:
|
|||||||
uint8_t maskandmode[DMX_MAX_FILTER_SIZE];
|
uint8_t maskandmode[DMX_MAX_FILTER_SIZE];
|
||||||
uint8_t maskandnotmode[DMX_MAX_FILTER_SIZE];
|
uint8_t maskandnotmode[DMX_MAX_FILTER_SIZE];
|
||||||
|
|
||||||
|
cRingBufferLinear *ringbuffer;
|
||||||
|
|
||||||
inline uint16_t GetLength(const uint8_t *Data);
|
inline uint16_t GetLength(const uint8_t *Data);
|
||||||
void New(void);
|
void New(void);
|
||||||
int Filter(void);
|
int Filter(void);
|
||||||
@ -54,11 +54,10 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// constructor & destructor
|
// constructor & destructor
|
||||||
cIptvSectionFilter(int Index, int DeviceIndex, uint16_t Pid,
|
cIptvSectionFilter(int DeviceIndex, uint16_t Pid, uint8_t Tid, uint8_t Mask);
|
||||||
uint8_t Tid, uint8_t Mask);
|
|
||||||
virtual ~cIptvSectionFilter();
|
virtual ~cIptvSectionFilter();
|
||||||
void Process(const uint8_t* Data);
|
void Process(const uint8_t* Data);
|
||||||
int GetReadDesc(void);
|
int Read(void *Buffer, size_t Length);
|
||||||
uint16_t GetPid(void) const { return pid; }
|
uint16_t GetPid(void) const { return pid; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
128
socket.c
128
socket.c
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -63,7 +64,7 @@ bool cIptvSocket::OpenSocket(const int Port, const bool isUdp)
|
|||||||
CloseSocket(), return false);
|
CloseSocket(), return false);
|
||||||
#endif // __FreeBSD__
|
#endif // __FreeBSD__
|
||||||
// Bind socket
|
// Bind socket
|
||||||
memset(&sockAddr, '\0', sizeof(sockAddr));
|
memset(&sockAddr, 0, sizeof(sockAddr));
|
||||||
sockAddr.sin_family = AF_INET;
|
sockAddr.sin_family = AF_INET;
|
||||||
sockAddr.sin_port = htons((uint16_t)(Port & 0xFFFF));
|
sockAddr.sin_port = htons((uint16_t)(Port & 0xFFFF));
|
||||||
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
@ -89,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
|
// UDP socket class
|
||||||
cIptvUdpSocket::cIptvUdpSocket()
|
cIptvUdpSocket::cIptvUdpSocket()
|
||||||
: streamAddr(INADDR_ANY)
|
: streamAddr(htonl(INADDR_ANY)),
|
||||||
|
sourceAddr(htonl(INADDR_ANY)),
|
||||||
|
useIGMPv3(false)
|
||||||
{
|
{
|
||||||
debug("cIptvUdpSocket::cIptvUdpSocket()\n");
|
debug("cIptvUdpSocket::cIptvUdpSocket()\n");
|
||||||
}
|
}
|
||||||
@ -101,17 +126,30 @@ cIptvUdpSocket::~cIptvUdpSocket()
|
|||||||
debug("cIptvUdpSocket::~cIptvUdpSocket()\n");
|
debug("cIptvUdpSocket::~cIptvUdpSocket()\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIptvUdpSocket::OpenSocket(const int Port, const in_addr_t StreamAddr)
|
bool cIptvUdpSocket::OpenSocket(const int Port)
|
||||||
{
|
{
|
||||||
debug("cIptvUdpSocket::OpenSocket()\n");
|
debug("cIptvUdpSocket::OpenSocket()\n");
|
||||||
streamAddr = StreamAddr;
|
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);
|
return cIptvSocket::OpenSocket(Port, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cIptvUdpSocket::CloseSocket(void)
|
void cIptvUdpSocket::CloseSocket(void)
|
||||||
{
|
{
|
||||||
debug("cIptvUdpSocket::CloseSocket()\n");
|
debug("cIptvUdpSocket::CloseSocket()\n");
|
||||||
streamAddr = INADDR_ANY;
|
streamAddr = htonl(INADDR_ANY);
|
||||||
|
sourceAddr = htonl(INADDR_ANY);
|
||||||
|
useIGMPv3 = false;
|
||||||
cIptvSocket::CloseSocket();
|
cIptvSocket::CloseSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,10 +159,28 @@ bool cIptvUdpSocket::JoinMulticast(void)
|
|||||||
// Check if socket exists
|
// Check if socket exists
|
||||||
if (!isActive && (socketDesc >= 0)) {
|
if (!isActive && (socketDesc >= 0)) {
|
||||||
// Join a new multicast group
|
// Join a new multicast group
|
||||||
struct ip_mreq mreq;
|
if (useIGMPv3) {
|
||||||
mreq.imr_multiaddr.s_addr = streamAddr;
|
// Source-specific multicast (SSM) is used
|
||||||
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
struct group_source_req gsr;
|
||||||
ERROR_IF_RET(setsockopt(socketDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_ADD_MEMBERSHIP)", return false);
|
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
|
// Update multicasting flag
|
||||||
isActive = true;
|
isActive = true;
|
||||||
}
|
}
|
||||||
@ -137,10 +193,28 @@ bool cIptvUdpSocket::DropMulticast(void)
|
|||||||
// Check if socket exists
|
// Check if socket exists
|
||||||
if (isActive && (socketDesc >= 0)) {
|
if (isActive && (socketDesc >= 0)) {
|
||||||
// Drop the existing multicast group
|
// Drop the existing multicast group
|
||||||
struct ip_mreq mreq;
|
if (useIGMPv3) {
|
||||||
mreq.imr_multiaddr.s_addr = streamAddr;
|
// Source-specific multicast (SSM) is used
|
||||||
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
struct group_source_req gsr;
|
||||||
ERROR_IF_RET(setsockopt(socketDesc, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0, "setsockopt(IP_DROP_MEMBERSHIP)", return false);
|
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
|
// Update multicasting flag
|
||||||
isActive = false;
|
isActive = false;
|
||||||
}
|
}
|
||||||
@ -161,7 +235,6 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
|||||||
do {
|
do {
|
||||||
socklen_t addrlen = sizeof(sockAddr);
|
socklen_t addrlen = sizeof(sockAddr);
|
||||||
struct msghdr msgh;
|
struct msghdr msgh;
|
||||||
struct cmsghdr *cmsg;
|
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
char cbuf[256];
|
char cbuf[256];
|
||||||
len = 0;
|
len = 0;
|
||||||
@ -184,10 +257,10 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
|||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
#ifndef __FreeBSD__
|
#ifndef __FreeBSD__
|
||||||
// Process auxiliary received data and validate source address
|
// Process auxiliary received data and validate source address
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
|
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)) {
|
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
|
||||||
struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||||
if ((i->ipi_addr.s_addr == streamAddr) || (INADDR_ANY == streamAddr)) {
|
if ((i->ipi_addr.s_addr == streamAddr) || (htonl(INADDR_ANY) == streamAddr)) {
|
||||||
#endif // __FreeBSD__
|
#endif // __FreeBSD__
|
||||||
if (BufferAddr[0] == TS_SYNC_BYTE)
|
if (BufferAddr[0] == TS_SYNC_BYTE)
|
||||||
return len;
|
return len;
|
||||||
@ -245,29 +318,8 @@ cIptvTcpSocket::~cIptvTcpSocket()
|
|||||||
bool cIptvTcpSocket::OpenSocket(const int Port, const char *StreamAddr)
|
bool cIptvTcpSocket::OpenSocket(const int Port, const char *StreamAddr)
|
||||||
{
|
{
|
||||||
debug("cIptvTcpSocket::OpenSocket()\n");
|
debug("cIptvTcpSocket::OpenSocket()\n");
|
||||||
|
|
||||||
// Socket must be opened before setting the host address
|
// Socket must be opened before setting the host address
|
||||||
bool retval = cIptvSocket::OpenSocket(Port, false);
|
return (cIptvSocket::OpenSocket(Port, false) && CheckAddress(StreamAddr, &sockAddr.sin_addr.s_addr));
|
||||||
|
|
||||||
// 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 retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cIptvTcpSocket::CloseSocket(void)
|
void cIptvTcpSocket::CloseSocket(void)
|
||||||
|
6
socket.h
6
socket.h
@ -25,6 +25,7 @@ protected:
|
|||||||
protected:
|
protected:
|
||||||
bool OpenSocket(const int Port, const bool isUdp);
|
bool OpenSocket(const int Port, const bool isUdp);
|
||||||
void CloseSocket(void);
|
void CloseSocket(void);
|
||||||
|
bool CheckAddress(const char *Addr, in_addr_t *InAddr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cIptvSocket();
|
cIptvSocket();
|
||||||
@ -34,12 +35,15 @@ public:
|
|||||||
class cIptvUdpSocket : public cIptvSocket {
|
class cIptvUdpSocket : public cIptvSocket {
|
||||||
private:
|
private:
|
||||||
in_addr_t streamAddr;
|
in_addr_t streamAddr;
|
||||||
|
in_addr_t sourceAddr;
|
||||||
|
bool useIGMPv3;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cIptvUdpSocket();
|
cIptvUdpSocket();
|
||||||
virtual ~cIptvUdpSocket();
|
virtual ~cIptvUdpSocket();
|
||||||
virtual int Read(unsigned char* BufferAddr, unsigned int BufferLen);
|
virtual int Read(unsigned char* BufferAddr, unsigned int BufferLen);
|
||||||
bool OpenSocket(const int Port, const in_addr_t StreamAddr = INADDR_ANY);
|
bool OpenSocket(const int Port);
|
||||||
|
bool OpenSocket(const int Port, const char *StreamAddr, const char *SourceAddr, bool UseIGMPv3);
|
||||||
void CloseSocket(void);
|
void CloseSocket(void);
|
||||||
bool JoinMulticast(void);
|
bool JoinMulticast(void);
|
||||||
bool DropMulticast(void);
|
bool DropMulticast(void);
|
||||||
|
Loading…
Reference in New Issue
Block a user