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
|
||||
|
||||
- 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
|
||||
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)
|
||||
@ -119,9 +120,9 @@ 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
|
||||
@ -167,6 +168,11 @@ Notes:
|
||||
- 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.
|
||||
|
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) {
|
||||
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)
|
||||
@ -395,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);
|
||||
|
6
device.h
6
device.h
@ -76,6 +76,7 @@ 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;
|
||||
@ -101,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
|
||||
|
8
iptv.c
8
iptv.c
@ -13,15 +13,15 @@
|
||||
#include "device.h"
|
||||
#include "iptvservice.h"
|
||||
|
||||
#if defined(APIVERSNUM) && APIVERSNUM < 10727
|
||||
#error "VDR-1.7.27 API version or greater is required!"
|
||||
#if defined(APIVERSNUM) && APIVERSNUM < 10730
|
||||
#error "VDR-1.7.30 API version or greater is required!"
|
||||
#endif
|
||||
|
||||
#ifndef GITVERSION
|
||||
#define GITVERSION ""
|
||||
#endif
|
||||
|
||||
const char VERSION[] = "1.0.1" GITVERSION;
|
||||
const char VERSION[] = "1.1.0" GITVERSION;
|
||||
static const char DESCRIPTION[] = trNOOP("Experience the IPTV");
|
||||
|
||||
class cPluginIptv : public cPlugin {
|
||||
@ -99,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);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,9 @@
|
||||
#include "socket.h"
|
||||
|
||||
cIptvProtocolUdp::cIptvProtocolUdp()
|
||||
: streamAddr(strdup("")),
|
||||
: isIGMPv3(false),
|
||||
sourceAddr(strdup("")),
|
||||
streamAddr(strdup("")),
|
||||
streamPort(0)
|
||||
{
|
||||
debug("cIptvProtocolUdp::cIptvProtocolUdp()\n");
|
||||
@ -32,12 +34,13 @@ cIptvProtocolUdp::~cIptvProtocolUdp()
|
||||
cIptvProtocolUdp::Close();
|
||||
// Free allocated memory
|
||||
free(streamAddr);
|
||||
free(sourceAddr);
|
||||
}
|
||||
|
||||
bool cIptvProtocolUdp::Open(void)
|
||||
{
|
||||
debug("cIptvProtocolUdp::Open(): streamAddr=%s\n", streamAddr);
|
||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
||||
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||
if (!isempty(streamAddr)) {
|
||||
// Join a new multicast group
|
||||
JoinMulticast();
|
||||
@ -50,12 +53,13 @@ bool cIptvProtocolUdp::Close(void)
|
||||
debug("cIptvProtocolUdp::Close(): streamAddr=%s\n", streamAddr);
|
||||
if (!isempty(streamAddr)) {
|
||||
// Drop the multicast group
|
||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
||||
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||
DropMulticast();
|
||||
}
|
||||
// Close the socket
|
||||
CloseSocket();
|
||||
// Do NOT reset stream and source addresses
|
||||
//sourceAddr = strcpyrealloc(sourceAddr, "");
|
||||
//streamAddr = strcpyrealloc(streamAddr, "");
|
||||
//streamPort = 0;
|
||||
return true;
|
||||
@ -72,15 +76,27 @@ bool cIptvProtocolUdp::Set(const char* Location, const int Parameter, const int
|
||||
if (!isempty(Location)) {
|
||||
// Drop the multicast group
|
||||
if (!isempty(streamAddr)) {
|
||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
||||
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||
DropMulticast();
|
||||
}
|
||||
// Update stream address and port
|
||||
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;
|
||||
// Join a new multicast group
|
||||
if (!isempty(streamAddr)) {
|
||||
OpenSocket(streamPort, inet_addr(streamAddr));
|
||||
OpenSocket(streamPort, streamAddr, sourceAddr, isIGMPv3);
|
||||
JoinMulticast();
|
||||
}
|
||||
}
|
||||
@ -90,5 +106,7 @@ bool cIptvProtocolUdp::Set(const char* Location, const int Parameter, const int
|
||||
cString cIptvProtocolUdp::GetInformation(void)
|
||||
{
|
||||
//debug("cIptvProtocolUdp::GetInformation()");
|
||||
if (isIGMPv3)
|
||||
return cString::sprintf("udp://%s@%s:%d", sourceAddr, streamAddr, streamPort);
|
||||
return cString::sprintf("udp://%s:%d", streamAddr, streamPort);
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
class cIptvProtocolUdp : public cIptvUdpSocket, public cIptvProtocolIf {
|
||||
private:
|
||||
bool isIGMPv3;
|
||||
char* sourceAddr;
|
||||
char* streamAddr;
|
||||
int streamPort;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -36,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];
|
||||
@ -46,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);
|
||||
@ -54,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; }
|
||||
};
|
||||
|
||||
|
128
socket.c
128
socket.c
@ -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>
|
||||
@ -63,7 +64,7 @@ bool cIptvSocket::OpenSocket(const int Port, const bool isUdp)
|
||||
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);
|
||||
@ -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
|
||||
cIptvUdpSocket::cIptvUdpSocket()
|
||||
: streamAddr(INADDR_ANY)
|
||||
: streamAddr(htonl(INADDR_ANY)),
|
||||
sourceAddr(htonl(INADDR_ANY)),
|
||||
useIGMPv3(false)
|
||||
{
|
||||
debug("cIptvUdpSocket::cIptvUdpSocket()\n");
|
||||
}
|
||||
@ -101,17 +126,30 @@ cIptvUdpSocket::~cIptvUdpSocket()
|
||||
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");
|
||||
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);
|
||||
}
|
||||
|
||||
void cIptvUdpSocket::CloseSocket(void)
|
||||
{
|
||||
debug("cIptvUdpSocket::CloseSocket()\n");
|
||||
streamAddr = INADDR_ANY;
|
||||
streamAddr = htonl(INADDR_ANY);
|
||||
sourceAddr = htonl(INADDR_ANY);
|
||||
useIGMPv3 = false;
|
||||
cIptvSocket::CloseSocket();
|
||||
}
|
||||
|
||||
@ -121,10 +159,28 @@ bool cIptvUdpSocket::JoinMulticast(void)
|
||||
// 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;
|
||||
}
|
||||
@ -137,10 +193,28 @@ bool cIptvUdpSocket::DropMulticast(void)
|
||||
// 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;
|
||||
}
|
||||
@ -161,7 +235,6 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
||||
do {
|
||||
socklen_t addrlen = sizeof(sockAddr);
|
||||
struct msghdr msgh;
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
char cbuf[256];
|
||||
len = 0;
|
||||
@ -184,10 +257,10 @@ int cIptvUdpSocket::Read(unsigned char* BufferAddr, unsigned int BufferLen)
|
||||
if (len > 0) {
|
||||
#ifndef __FreeBSD__
|
||||
// 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)) {
|
||||
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__
|
||||
if (BufferAddr[0] == TS_SYNC_BYTE)
|
||||
return len;
|
||||
@ -245,29 +318,8 @@ cIptvTcpSocket::~cIptvTcpSocket()
|
||||
bool cIptvTcpSocket::OpenSocket(const int Port, const char *StreamAddr)
|
||||
{
|
||||
debug("cIptvTcpSocket::OpenSocket()\n");
|
||||
|
||||
// Socket must be opened before setting the host address
|
||||
bool retval = cIptvSocket::OpenSocket(Port, false);
|
||||
|
||||
// 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;
|
||||
return (cIptvSocket::OpenSocket(Port, false) && CheckAddress(StreamAddr, &sockAddr.sin_addr.s_addr));
|
||||
}
|
||||
|
||||
void cIptvTcpSocket::CloseSocket(void)
|
||||
|
6
socket.h
6
socket.h
@ -25,6 +25,7 @@ protected:
|
||||
protected:
|
||||
bool OpenSocket(const int Port, const bool isUdp);
|
||||
void CloseSocket(void);
|
||||
bool CheckAddress(const char *Addr, in_addr_t *InAddr);
|
||||
|
||||
public:
|
||||
cIptvSocket();
|
||||
@ -34,12 +35,15 @@ 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 StreamAddr = 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(void);
|
||||
bool DropMulticast(void);
|
||||
|
Loading…
Reference in New Issue
Block a user