summaryrefslogtreecommitdiff
path: root/mcast
diff options
context:
space:
mode:
authorLars Heer <l.heer@gmx.de>2013-09-18 14:47:42 +0200
committerLars Heer <l.heer@gmx.de>2013-09-18 14:47:42 +0200
commitc49649d7324c481a2a39145b77259eb58655df22 (patch)
tree52e6c3063d0608748c67e9658defdb2ffa4cfbd7 /mcast
parent58aa9d6ebc3bb122067c72b21af84e51aa655ad0 (diff)
downloadvdr-plugin-mcli-c49649d7324c481a2a39145b77259eb58655df22.tar.gz
vdr-plugin-mcli-c49649d7324c481a2a39145b77259eb58655df22.tar.bz2
added netcv2dvbip revision 188 from https://svn.baycom.de/repos/vdr-mcli-plugin/mcast/netcv2dvbip
Diffstat (limited to 'mcast')
-rw-r--r--mcast/netcv2dvbip/.indent.pro1
-rw-r--r--mcast/netcv2dvbip/README132
-rw-r--r--mcast/netcv2dvbip/channels.conf3
-rw-r--r--mcast/netcv2dvbip/clist.c196
-rw-r--r--mcast/netcv2dvbip/clist.h60
l---------mcast/netcv2dvbip/crc32.c1
-rw-r--r--mcast/netcv2dvbip/dvbipstream.h99
-rw-r--r--mcast/netcv2dvbip/headers.h33
-rw-r--r--mcast/netcv2dvbip/iface.c262
-rw-r--r--mcast/netcv2dvbip/iface.h22
-rw-r--r--mcast/netcv2dvbip/igmp.c890
-rw-r--r--mcast/netcv2dvbip/igmp.h123
-rw-r--r--mcast/netcv2dvbip/main.c400
-rw-r--r--mcast/netcv2dvbip/makefile61
-rw-r--r--mcast/netcv2dvbip/mclilink.c659
-rw-r--r--mcast/netcv2dvbip/mingw/Makefile19
-rw-r--r--mcast/netcv2dvbip/mingw/build.cmd2
-rw-r--r--mcast/netcv2dvbip/mingw/clean.cmd2
-rw-r--r--mcast/netcv2dvbip/misc.c193
-rw-r--r--mcast/netcv2dvbip/misc.h67
-rw-r--r--mcast/netcv2dvbip/parse.c496
l---------mcast/netcv2dvbip/siparser.c1
-rw-r--r--mcast/netcv2dvbip/stream.c185
-rw-r--r--mcast/netcv2dvbip/stream.h31
-rw-r--r--mcast/netcv2dvbip/streamer.c101
-rw-r--r--mcast/netcv2dvbip/streamer.h35
-rw-r--r--mcast/netcv2dvbip/thread.c327
-rw-r--r--mcast/netcv2dvbip/thread.h131
28 files changed, 4532 insertions, 0 deletions
diff --git a/mcast/netcv2dvbip/.indent.pro b/mcast/netcv2dvbip/.indent.pro
new file mode 100644
index 0000000..2faef85
--- /dev/null
+++ b/mcast/netcv2dvbip/.indent.pro
@@ -0,0 +1 @@
+-i8 -br -l0 -ce -npsl
diff --git a/mcast/netcv2dvbip/README b/mcast/netcv2dvbip/README
new file mode 100644
index 0000000..1880e48
--- /dev/null
+++ b/mcast/netcv2dvbip/README
@@ -0,0 +1,132 @@
+This is a tool for streaming DVB TS packets via udp multicasts on demand from the netceiver.
+It listens for IGMP(v1/v2/v3) join/leave multicast group messages on the network and starts/stops
+the corresponding streams accordingly. It uses a channels.conf file for channel information.
+Each channel is mapped to one multicast group starting with 239.255.0.1 and so on.
+
+Written by:
+Christian Cier-Zniewski <c.cier@gmx.de>
+Project homepage: http://nanodev.nfshost.com/netcv2dvbip/
+SVN source code repository: https://svn.baycom.de/repos/vdr-mcli-plugin/mcast/
+
+The software is based on source code from the following projects:
+
+IGMP component of vdr-streamdev-plugin
+Frank Schmirler <vdrdev@schmirler.de>
+
+VDR
+Klaus Schmidinger <kls@cadsoft.de>
+
+Netceiver mcli-lib
+Baycom GmbH <info@baycom.de>
+
+-----------------------------------------------------------
+
+Quick Compile Howto (Linux):
+
+1)
+svn co https://svn.baycom.de/repos/vdr-mcli-plugin/mcast/
+
+2)
+cd mcast
+
+3)
+cd client
+make
+cd ..
+
+4)
+cd netcv2dvbip
+make
+
+5)
+./netcv2dvbip
+
+------------------------------------------------------------
+
+Notes:
+
+ * Each UDP packet contains 7*188=1316 bytes ( 7 TS packets)
+ * channels.conf: only frontend paramters are needed. PIDs are
+ extracted from the PAT/PMT on demand.
+ * Windows: MS Loopback Adapter Driver has to be installed if you only
+ want to stream local multicasts.
+ Configure a static unused IP for this adapter and add a route for
+ 239.255.0.0/16 to this ip
+ eg.: route add 239.255.0.0 mask 255.255.0.0 10.11.12.13
+ where 10.11.12.13 is the static IP address of the loopback
+ adapter.
+ Linux: specify the option "-b lo" and make sure that the
+ multicast flag is set on the lo device.
+ If not, type: ifconfig lo multicast
+ * Windows: netcv2dvb for Windows is compiled using Visual C++ 2008 Express Edition
+ Therefore it needs the Microsoft Visual C++ 2008 Runtime Redistributable to be installed.
+
+Known Issues:
+
+ * Linux: netcv2dvbip make use of the multicast routing API to be
+ able to receive all IGMP (v1,v2) messages on the subnet without joining
+ all groups. If you have configured 200 channels, then at least
+ those 200 groups would have to be joined to receive the group
+ specific queries. IGMPv3 does not suffer this "problem", since
+ all reports are sent to the group 224.0.0.22.
+ So, if you are already using software that make use of this API,
+ netcv2dvbip will fail to start, because only one program can make
+ use of this API.
+ * Windows XP does not support MLDv2 messages, so the built-in MLD-Reporter of
+ libmcli is used in the Windows version of netcv2dvbip.
+ Windows Vista and Windows 7 already support MLDv2.
+ * IMPORTANT note for VLAN users: Windows does not support VLANs as Linux does.
+ So, if you already using a VLAN-enabled network for the Netceiver and Reel-Netclients
+ then you must use a LAN card which offers VLAN support in the drivers.
+ Eg.: most Intel-adapters support this ( I am using a EXPI9301CT)
+
+------------------------------------------------------------
+
+Possible clients:
+
+ * VLC [Linux, Windows]
+ * vdr-iptv-plugin [Linux]
+ * DVBLink for IPTV (http://www.dvblogic.com/) [Windows]
+ * Mediaportal with IPTV source filter (with EPG) [Windows]
+ * [...]
+
+------------------------------------------------------------
+
+Command line Options:
+
+ -b <interface on which to listen for IGMP mesages and to send streams to>
+ Default: first found running interface
+
+ -p <port to send streams to>
+ Default: 12345
+
+ -i <interface with netceiver network>
+ Default: first found IPv6 interface
+
+ -c <channels.conf file>
+
+ -e include EIT packets in streams (EPG)
+
+ -h help
+
+
+ChangeLog:
+==========
+2009-??-?? version 1.0.0
+ - initial release
+
+2010-06-03 version 1.1.0
+ - bugfixes: * number of channels > 255 is now handled correctly
+ * PMT PIDs > 255 had a wrong entry in the PAT
+ - new features: * DVB-C and DVB-T support
+ * support for VDR-1.7.x channels.conf format
+ (older formats and ReelVDR format are still supported)
+ * playlist file generation (M3U)
+ * EPG support: new command line option "-e" activates
+ sending of EIT packets (PID 0x12) in the stream.
+2010-06-17 version 1.1.1 * bugfix: port number was not set correctly in M3U file
+ * bugfix: high CPU load (select() timeout was not
+ set correctly)
+ * changed: streams now also use non-blocking sockets
+ * Windows only: activate built-in MLDv2 reporter only for
+ Windows XP
diff --git a/mcast/netcv2dvbip/channels.conf b/mcast/netcv2dvbip/channels.conf
new file mode 100644
index 0000000..520e108
--- /dev/null
+++ b/mcast/netcv2dvbip/channels.conf
@@ -0,0 +1,3 @@
+:Hauptsender
+Das Erste;ARD:11836:hC34:S19.2E:27500:101:102=deu,103=2ch;106=deu:104:0:28106:1:1101:0
+ZDF;ZDFvision:11954:hC34:S19.2E:27500:110:120=deu,121=2ch;125=deu:130:0:28006:1:1079:0
diff --git a/mcast/netcv2dvbip/clist.c b/mcast/netcv2dvbip/clist.c
new file mode 100644
index 0000000..1916e08
--- /dev/null
+++ b/mcast/netcv2dvbip/clist.c
@@ -0,0 +1,196 @@
+/*
+ * tools.c: Various tools
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: tools.c 2.3 2009/05/31 11:43:24 kls Exp $
+ */
+
+#include <stdlib.h>
+
+#include "clist.h"
+
+
+// --- cListObject -----------------------------------------------------------
+
+cListObject::cListObject(void)
+{
+ prev = next = NULL;
+}
+
+cListObject::~cListObject()
+{
+}
+
+void cListObject::Append(cListObject *Object)
+{
+ next = Object;
+ Object->prev = this;
+}
+
+void cListObject::Insert(cListObject *Object)
+{
+ prev = Object;
+ Object->next = this;
+}
+
+void cListObject::Unlink(void)
+{
+ if (next)
+ next->prev = prev;
+ if (prev)
+ prev->next = next;
+ next = prev = NULL;
+}
+
+int cListObject::Index(void) const
+{
+ cListObject *p = prev;
+ int i = 0;
+
+ while (p) {
+ i++;
+ p = p->prev;
+ }
+ return i;
+}
+
+// --- cListBase -------------------------------------------------------------
+
+cListBase::cListBase(void)
+{
+ objects = lastObject = NULL;
+ count = 0;
+}
+
+cListBase::~cListBase()
+{
+ Clear();
+}
+
+void cListBase::Add(cListObject *Object, cListObject *After)
+{
+ if (After && After != lastObject) {
+ After->Next()->Insert(Object);
+ After->Append(Object);
+ }
+ else {
+ if (lastObject)
+ lastObject->Append(Object);
+ else
+ objects = Object;
+ lastObject = Object;
+ }
+ count++;
+}
+
+void cListBase::Ins(cListObject *Object, cListObject *Before)
+{
+ if (Before && Before != objects) {
+ Before->Prev()->Append(Object);
+ Before->Insert(Object);
+ }
+ else {
+ if (objects)
+ objects->Insert(Object);
+ else
+ lastObject = Object;
+ objects = Object;
+ }
+ count++;
+}
+
+void cListBase::Del(cListObject *Object, bool DeleteObject)
+{
+ if (Object == objects)
+ objects = Object->Next();
+ if (Object == lastObject)
+ lastObject = Object->Prev();
+ Object->Unlink();
+ if (DeleteObject)
+ delete Object;
+ count--;
+}
+
+void cListBase::Move(int From, int To)
+{
+ Move(Get(From), Get(To));
+}
+
+void cListBase::Move(cListObject *From, cListObject *To)
+{
+ if (From && To) {
+ if (From->Index() < To->Index())
+ To = To->Next();
+ if (From == objects)
+ objects = From->Next();
+ if (From == lastObject)
+ lastObject = From->Prev();
+ From->Unlink();
+ if (To) {
+ if (To->Prev())
+ To->Prev()->Append(From);
+ From->Append(To);
+ }
+ else {
+ lastObject->Append(From);
+ lastObject = From;
+ }
+ if (!From->Prev())
+ objects = From;
+ }
+}
+
+void cListBase::Clear(void)
+{
+ while (objects) {
+ cListObject *object = objects->Next();
+ delete objects;
+ objects = object;
+ }
+ objects = lastObject = NULL;
+ count = 0;
+}
+
+cListObject *cListBase::Get(int Index) const
+{
+ if (Index < 0)
+ return NULL;
+ cListObject *object = objects;
+ while (object && Index-- > 0)
+ object = object->Next();
+ return object;
+}
+
+static int CompareListObjects(const void *a, const void *b)
+{
+ const cListObject *la = *(const cListObject **)a;
+ const cListObject *lb = *(const cListObject **)b;
+ return la->Compare(*lb);
+}
+
+void cListBase::Sort(void)
+{
+ int n = Count();
+#ifndef WIN32
+ cListObject *a[n];
+#else
+ cListObject **a;
+ a = new cListObject*[n];
+#endif
+ cListObject *object = objects;
+ int i = 0;
+ while (object && i < n) {
+ a[i++] = object;
+ object = object->Next();
+ }
+ qsort(a, n, sizeof(cListObject *), CompareListObjects);
+ objects = lastObject = NULL;
+ for (i = 0; i < n; i++) {
+ a[i]->Unlink();
+ count--;
+ Add(a[i]);
+ }
+}
+
diff --git a/mcast/netcv2dvbip/clist.h b/mcast/netcv2dvbip/clist.h
new file mode 100644
index 0000000..d44da28
--- /dev/null
+++ b/mcast/netcv2dvbip/clist.h
@@ -0,0 +1,60 @@
+/*
+ * tools.h: Various tools
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: tools.h 2.2 2009/04/14 20:41:39 kls Exp $
+ */
+
+#ifndef __LIST_H
+#define __LIST_H
+
+//#include "misc.h"
+
+class cListObject {
+private:
+ cListObject *prev, *next;
+public:
+ cListObject(void);
+ virtual ~cListObject();
+ virtual int Compare(const cListObject &ListObject) const { return 0; }
+ ///< Must return 0 if this object is equal to ListObject, a positive value
+ ///< if it is "greater", and a negative value if it is "smaller".
+ void Append(cListObject *Object);
+ void Insert(cListObject *Object);
+ void Unlink(void);
+ int Index(void) const;
+ cListObject *Prev(void) const { return prev; }
+ cListObject *Next(void) const { return next; }
+ };
+
+class cListBase {
+protected:
+ cListObject *objects, *lastObject;
+ cListBase(void);
+ int count;
+public:
+ virtual ~cListBase();
+ void Add(cListObject *Object, cListObject *After = NULL);
+ void Ins(cListObject *Object, cListObject *Before = NULL);
+ void Del(cListObject *Object, bool DeleteObject = true);
+ virtual void Move(int From, int To);
+ void Move(cListObject *From, cListObject *To);
+ virtual void Clear(void);
+ cListObject *Get(int Index) const;
+ int Count(void) const { return count; }
+ void Sort(void);
+ };
+
+template<class T> class cList : public cListBase {
+public:
+ T *Get(int Index) const { return (T *)cListBase::Get(Index); }
+ T *First(void) const { return (T *)objects; }
+ T *Last(void) const { return (T *)lastObject; }
+ T *Prev(const T *object) const { return (T *)object->cListObject::Prev(); } // need to call cListObject's members to
+ T *Next(const T *object) const { return (T *)object->cListObject::Next(); } // avoid ambiguities in case of a "list of lists"
+ };
+
+
+#endif //__LIST_H
diff --git a/mcast/netcv2dvbip/crc32.c b/mcast/netcv2dvbip/crc32.c
new file mode 120000
index 0000000..0bc6c1f
--- /dev/null
+++ b/mcast/netcv2dvbip/crc32.c
@@ -0,0 +1 @@
+../common/crc32.c \ No newline at end of file
diff --git a/mcast/netcv2dvbip/dvbipstream.h b/mcast/netcv2dvbip/dvbipstream.h
new file mode 100644
index 0000000..a374617
--- /dev/null
+++ b/mcast/netcv2dvbip/dvbipstream.h
@@ -0,0 +1,99 @@
+#ifndef _DVBIPSTREAM_H
+#define _DVBIPSTREAM_H
+
+#include "headers.h"
+#include "misc.h"
+
+#define MAXAPIDS 32
+#define MAXDPIDS 4
+#define MAXCAIDS 2
+
+#define TS_PER_UDP 7
+
+typedef struct
+{
+ int number;
+ char *name;
+ char *provider;
+ char *shortName;
+ int type;
+ unsigned int frequency;
+ int srate;
+ int coderateH;
+ int coderateL;
+ int guard;
+ int polarization;
+ int inversion;
+ int modulation;
+ int source;
+ int transmission;
+ int bandwidth;
+ int hierarchy;
+ int vpid;
+ int ppid;
+ int sid;
+ int rid;
+ int tid;
+ int tpid;
+ int nid;
+ int caids[MAXCAIDS];
+ int NumCaids;
+ int apids[MAXAPIDS];
+ int NumApids;
+ int dpids[MAXDPIDS];
+ int NumDpids;
+ int eitpids[1];
+ int NumEitpids;
+ int sdtpids[1];
+ int NumSdtpids;
+} channel_t;
+
+
+typedef struct _stream_buffer_t
+{
+ struct _stream_buffer_t *next;
+ char data[TS_PER_UDP*188];
+} stream_buffer_t;
+
+typedef struct
+{
+ recv_info_t *r;
+ stream_buffer_t *free;
+ stream_buffer_t *head;
+ stream_buffer_t *tail;
+ stream_buffer_t *wr;
+ stream_buffer_t *rd;
+ int fill;
+ int si_state;
+ psi_buf_t psi;
+ channel_t *cdata;
+ int pmt_pid;
+ int es_pids[32];
+ int es_pidnum;
+ int fta;
+ pthread_t t;
+ int stop;
+ int ts_cnt;
+ pthread_mutex_t lock_bf;
+ pthread_mutex_t lock_ts;
+} stream_info_t;
+
+channel_t *read_channel_list (char *filename);
+
+int get_channel_num (void);
+int get_channel_name (int n, char *str, int maxlen);
+channel_t *get_channel_data (int n);
+
+// mcilink.c
+void mcli_startup (void);
+void* mcli_stream_setup (const int channum);
+size_t mcli_stream_access (void* handle, char **buf);
+size_t mcli_stream_part_access (void* handle, char **buf);
+void mcli_stream_skip (void* handle);
+int mcli_stream_stop (void* handle);
+
+// parse.c
+int ParseLine (const char *s, channel_t * ch);
+
+
+#endif
diff --git a/mcast/netcv2dvbip/headers.h b/mcast/netcv2dvbip/headers.h
new file mode 100644
index 0000000..5ffa21b
--- /dev/null
+++ b/mcast/netcv2dvbip/headers.h
@@ -0,0 +1,33 @@
+/*
+ * (c) BayCom GmbH, http://www.baycom.de, info@baycom.de
+ *
+ * See the COPYING file for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#ifndef __HEADERS_H__
+#define __HEADERS_H__
+
+#include "defs.h"
+#include "version.h"
+#include "list.h"
+#include "satlists.h"
+#include "mcast.h"
+#include "input.h"
+#include "recv_ccpp.h"
+#include "recv_tv.h"
+#include "tools.h"
+#include "interfaces.h"
+#include "mcast.h"
+#include "mld.h"
+#include "api_server.h"
+#include "tca_handler.h"
+#include "tra_handler.h"
+#include "mld_reporter.h"
+#include "ciparser.h"
+#include "ci_handler.h"
+#include "mmi_handler.h"
+#include "crc32.h"
+#include "siparser.h"
+#endif
diff --git a/mcast/netcv2dvbip/iface.c b/mcast/netcv2dvbip/iface.c
new file mode 100644
index 0000000..2cfa679
--- /dev/null
+++ b/mcast/netcv2dvbip/iface.c
@@ -0,0 +1,262 @@
+#include "dvbipstream.h"
+#include "iface.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <iphlpapi.h>
+#endif
+
+
+#ifndef WIN32
+int get_iface_list(struct ifconf *ifconf)
+{
+ int sock, rval;
+
+ sock = socket(AF_INET,SOCK_STREAM,0);
+ if(sock < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+
+ if((rval = ioctl(sock, SIOCGIFCONF , (char*) ifconf )) < 0 )
+ perror("ioctl(SIOGIFCONF)");
+
+ close(sock);
+
+ return rval;
+}
+
+int get_iface_ipaddress(struct ifreq *ifreq)
+{
+ int sock, rval;
+
+ sock = socket(AF_INET,SOCK_STREAM,0);
+ if(sock < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+
+ if((rval = ioctl(sock, SIOCGIFADDR , (char*) ifreq )) < 0 ) {
+ //perror("ioctl(SIOCGIFADDR)");
+ }
+ close(sock);
+
+ return rval;
+}
+
+int get_iface_flags(struct ifreq *ifreq)
+{
+ int sock, rval;
+
+ sock = socket(AF_INET,SOCK_STREAM,0);
+ if(sock < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+
+ if((rval = ioctl(sock, SIOCGIFFLAGS , (char*) ifreq )) < 0 )
+ perror("ioctl(SIOCGIFFLAGS)");
+
+ close(sock);
+
+ return rval;
+}
+
+int discover_interfaces(iface_t iflist[])
+{
+ struct ifreq ifreqs[MAXIF];
+ struct ifconf ifconf;
+ int nifaces, i;
+
+ memset(&ifconf,0,sizeof(ifconf));
+ ifconf.ifc_buf = (char*) (ifreqs);
+ ifconf.ifc_len = sizeof(ifreqs);
+
+ if(get_iface_list(&ifconf) < 0)
+ return 0;
+
+ nifaces = ifconf.ifc_len/sizeof(struct ifreq);
+
+ if (!quiet)
+ printf("Interfaces (count = %d):\n", nifaces);
+
+ for(i = 0; i < nifaces; i++)
+ {
+ strncpy(iflist[i].name, ifreqs[i].ifr_name, IFNAMSIZ);
+
+ u_char *addr;
+ if(get_iface_ipaddress(&ifreqs[i])<0)
+ continue;
+ addr = (u_char *) & (((struct sockaddr_in *)&
+ (ifreqs[i]).ifr_addr)->sin_addr);
+ if (!quiet)
+ printf("\t%i - %-10s : addr %d.%d.%d.%d\n",(i+1),
+ ifreqs[i].ifr_name,addr[0],addr[1],addr[2],
+ addr[3]);
+ iflist[i].ipaddr = ( (struct sockaddr_in *)&
+ (ifreqs[i]).ifr_addr)->sin_addr.s_addr;
+
+ }
+ return nifaces;
+}
+
+#endif
+
+#ifdef WIN32
+
+#define WORKING_BUFFER_SIZE 15000
+#define MAX_TRIES 3
+
+#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
+#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
+
+/* Note: could also use malloc() and free() */
+
+int discover_interfaces(iface_t iflist[])
+{
+ if (!quiet)
+ printf("Interfaces:\n");
+ /* Declare and initialize variables */
+
+ DWORD dwRetVal = 0;
+
+ unsigned int anum = 0;
+
+ // Set the flags to pass to GetAdaptersAddresses
+ ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
+
+ // default to IPv4 address family
+ ULONG family = AF_INET;
+
+ LPVOID lpMsgBuf = NULL;
+
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+ ULONG outBufLen = 0;
+ ULONG Iterations = 0;
+
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+ PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
+
+ // Allocate a 15 KB buffer to start with.
+ outBufLen = WORKING_BUFFER_SIZE;
+
+ do {
+
+ pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
+ if (pAddresses == NULL) {
+ printf
+ ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
+ return 0;
+ }
+
+ dwRetVal =
+ GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
+
+ if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+ FREE(pAddresses);
+ pAddresses = NULL;
+ } else {
+ break;
+ }
+
+ Iterations++;
+
+ } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
+
+ if (dwRetVal == NO_ERROR) {
+ // If successful, output some information from the data we received
+ pCurrAddresses = pAddresses;
+ while (pCurrAddresses) {
+ if (!quiet) {
+ printf("\tIfIndex (IPv4 interface): %u\n",
+ (unsigned int)pCurrAddresses->IfIndex);
+ printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName);
+ }
+ strncpy(iflist[anum].name, pCurrAddresses->AdapterName,
+ IFNAMSIZ);
+
+ pUnicast = pCurrAddresses->FirstUnicastAddress;
+
+ if (pCurrAddresses->OperStatus == IfOperStatusUp)
+ {
+ if (pUnicast != NULL) {
+ if (!quiet)
+ printf("\tUnicast Address: %s\n",
+ inet_ntoa(((struct sockaddr_in*)
+ (pUnicast->Address.lpSockaddr))->sin_addr) );
+ iflist[anum].ipaddr = ((struct sockaddr_in*)
+ (pUnicast->Address.lpSockaddr))->sin_addr.S_un.S_addr;
+ } else {
+ if (!quiet)
+ printf("\tNo Unicast Addresses\n");
+ }
+
+ if (!quiet) {
+#ifndef __MINGW32__
+ printf("\tDescription: %wS\n", pCurrAddresses->Description);
+ printf("\tFriendly name: %wS\n",
+ pCurrAddresses->FriendlyName);
+#else
+ printf("\tDescription: %ls\n", pCurrAddresses->Description);
+ printf("\tFriendly name: %ls\n",
+ pCurrAddresses->FriendlyName);
+#endif
+
+ printf("\tMtu: %lu\n", pCurrAddresses->Mtu);
+
+ printf("\n");
+ }
+
+ anum++;
+ }
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+ } else {
+ printf("Call to GetAdaptersAddresses failed with error: %d\n",
+ (int)dwRetVal);
+ if (dwRetVal == ERROR_NO_DATA)
+ printf("\tNo addresses were found for the requested parameters\n");
+ else {
+
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ // Default language
+ (LPTSTR) & lpMsgBuf, 0, NULL)) {
+ printf("\tError: %s", (char *)lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ if (pAddresses)
+ FREE(pAddresses);
+ return 0;
+ }
+ }
+ }
+
+ if (pAddresses) {
+ FREE(pAddresses);
+ }
+
+ return 1;
+}
+
+
+#endif
+
+
diff --git a/mcast/netcv2dvbip/iface.h b/mcast/netcv2dvbip/iface.h
new file mode 100644
index 0000000..23ce100
--- /dev/null
+++ b/mcast/netcv2dvbip/iface.h
@@ -0,0 +1,22 @@
+#ifndef __IFACE_H
+#define __IFACE_H
+
+#include "defs.h"
+
+#include "misc.h"
+
+// iface.c
+
+#define MAXIF 20
+
+typedef struct
+{
+ char name[IFNAMSIZ];
+ in_addr_t ipaddr;
+} iface_t;
+
+int discover_interfaces(iface_t *iflist);
+
+
+#endif
+
diff --git a/mcast/netcv2dvbip/igmp.c b/mcast/netcv2dvbip/igmp.c
new file mode 100644
index 0000000..45b0e60
--- /dev/null
+++ b/mcast/netcv2dvbip/igmp.c
@@ -0,0 +1,890 @@
+#include "defs.h"
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#ifndef __MINGW32__
+#include <mstcpip.h>
+#else
+#define SIO_RCVALL_IGMPMCAST _WSAIOW(IOC_VENDOR,3)
+#endif
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#ifdef APPLE
+#include <netinet/ip_mroute.h>
+#else
+#define _LINUX_IN_H
+#include <linux/mroute.h>
+#endif
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "igmp.h"
+#include "streamer.h"
+#include "misc.h"
+
+#ifndef IGMP_ALL_HOSTS
+#define IGMP_ALL_HOSTS htonl(0xE0000001L)
+#endif
+#ifndef IGMP_ALL_ROUTER
+#define IGMP_ALL_ROUTER htonl(0xE0000002L)
+#endif
+#ifndef IGMP_ALL_V3REPORTS
+#define IGMP_ALL_V3REPORTS htonl(0xE0000016L)
+#endif
+
+// IGMP parameters according to RFC2236. All time values in seconds.
+#define IGMP_ROBUSTNESS 2
+#define IGMP_QUERY_INTERVAL 125
+#define IGMP_QUERY_RESPONSE_INTERVAL 10
+#define IGMP_GROUP_MEMBERSHIP_INTERVAL \
+ (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL)
+#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL \
+ (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL / 2)
+#define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4)
+#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS
+// This value is 1/10 sec. RFC default is 10. Reduced to minimum to
+// free unused channels ASAP
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL_TS 1
+#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS
+
+
+// operations on struct timeval
+#define TV_CMP(a, cmp, b) \
+ (a.tv_sec == b.tv_sec ? a.tv_usec cmp b.tv_usec : a.tv_sec cmp b.tv_sec)
+#define TV_SET(tv) (tv.tv_sec || tv.tv_usec)
+#define TV_CLR(tv) memset(&tv, 0, sizeof(tv))
+#define TV_CPY(dst, src) memcpy(&dst, &src, sizeof(dst))
+#define TV_ADD(dst, ts) dst.tv_sec += ts / 10; \
+ dst.tv_usec += (ts % 10) * 100000; \
+ if (dst.tv_usec >= 1000000) { dst.tv_usec -= 1000000; dst.tv_sec++; }
+
+/* Taken from http://tools.ietf.org/html/rfc1071 */
+uint16_t inetChecksum(uint16_t *addr, int count)
+{
+ uint32_t sum = 0;
+ while (count > 1) {
+ sum += *addr++;
+ count -= 2;
+ }
+
+ if( count > 0 )
+ sum += * (uint8_t *) addr;
+
+ while (sum>>16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+
+cMulticastGroup::cMulticastGroup(in_addr_t Group):
+ group(Group),
+ reporter(0),
+ stream(NULL)
+{
+ TV_CLR(timeout);
+ TV_CLR(v1timer);
+ TV_CLR(retransmit);
+}
+
+cIgmpListener::cIgmpListener(cIgmpMain* igmpmain)
+ : cThread("IGMP listener")
+{
+ m_IgmpMain = igmpmain;
+}
+
+bool cIgmpListener::Membership(in_addr_t mcaddr, bool Add)
+{
+ int rc = 0;
+
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = mcaddr;
+ mreq.imr_interface.s_addr = m_bindaddr;
+
+ rc = ::setsockopt(m_socket, IPPROTO_IP,
+ Add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP,
+ (const char*) &mreq, sizeof(mreq));
+ if (rc < 0)
+ {
+ printf("IGMP: unable to %s %s", Add ? "join" : "leave", \
+ inet_ntoa(mreq.imr_multiaddr));
+#ifndef WIN32
+ if (errno == ENOBUFS)
+ printf("consider increasing sys.net.ipv4.igmp_max_memberships");
+ else
+#endif
+ log_socket_error("IGMP setsockopt(IP_ADD/DROP_MEMBERSHIP)");
+ return false;
+ }
+
+ return true;
+}
+
+void cIgmpListener::Destruct(void)
+{
+ int rc = 0;
+ in_addr_t defaultaddr = INADDR_ANY;
+
+ Cancel(3);
+
+ Membership(IGMP_ALL_HOSTS, false);
+ Membership(IGMP_ALL_ROUTER, false);
+ Membership(IGMP_ALL_V3REPORTS, false);
+
+ rc = setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&defaultaddr, sizeof(defaultaddr));
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(IP_MULTICAST_IF)");
+ }
+
+#ifdef WIN32
+ closesocket(m_socket);
+#else
+ rc = ::setsockopt( m_socket, IPPROTO_IP, MRT_DONE, NULL, 0);
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(MRT_DONE)");
+ }
+ close(m_socket);
+#endif
+}
+
+bool cIgmpListener::Initialize(iface_t bindif, int table)
+{
+ int rc = 0;
+#ifndef WIN32
+ int val = 0;
+#endif
+
+ m_bindaddr = bindif.ipaddr;
+
+ m_socket = ::socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+
+ if (m_socket < 0)
+ {
+ log_socket_error("IGMP socket()");
+ return false;
+ }
+
+#ifdef WIN32
+ //-------------------------
+ // Set the socket I/O mode: In this case FIONBIO
+ // enables or disables the blocking mode for the
+ // socket based on the numerical value of iMode.
+ // If iMode = 0, blocking is enabled;
+ // If iMode != 0, non-blocking mode is enabled.
+ u_long iMode = 1;
+ ioctlsocket(m_socket, FIONBIO, &iMode);
+#else
+ int x=fcntl(m_socket,F_GETFL,0); // Get socket flags
+ fcntl(m_socket,F_SETFL,x | O_NONBLOCK); // Add non-blocking flag
+#endif
+
+
+#ifndef WIN32
+#ifdef MRT_TABLE
+ if ( table )
+ {
+ rc = ::setsockopt( m_socket, IPPROTO_IP, MRT_TABLE, (void*)&table, sizeof(table) );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(MRT_TABLE)");
+ printf("IGMPv2 policy routing won't work! Please "
+ "enable multicast policy routing option in"
+ " the kernel configuration!\n");
+ }
+ }
+#endif
+ val = 1;
+ rc = ::setsockopt( m_socket, IPPROTO_IP, MRT_INIT,
+ (void*)&val, sizeof(val) );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(MRT_INIT)");
+ printf("IGMPv2 won't work! Please enable multicast router "
+ "option in the kernel configuration!\n");
+ }
+
+ struct vifctl VifCtl;
+ VifCtl.vifc_vifi = 0;
+ VifCtl.vifc_flags = 0; // no tunnel, no source routing, register ?
+ VifCtl.vifc_threshold = 1;// Packet TTL must be at least 1 to pass them
+ VifCtl.vifc_rate_limit = 0;// Ratelimit
+
+ VifCtl.vifc_lcl_addr.s_addr = m_bindaddr;
+ VifCtl.vifc_rmt_addr.s_addr = INADDR_ANY;
+
+ rc = ::setsockopt( m_socket, IPPROTO_IP, MRT_ADD_VIF,
+ (char *)&VifCtl, sizeof( VifCtl ) );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(MRT_ADD_VIF)");
+ }
+
+
+#endif
+/*
+ rc = setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&m_bindaddr, sizeof(m_bindaddr));
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(IP_MULTICAST_IF)");
+ return false;
+ }
+*/
+ /*
+ val = 1;
+ rc = ::setsockopt( m_socket, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&val, sizeof(val) );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(SO_REUSEADDR)");
+ return false;
+ }
+ */
+
+ /*
+ val = 1;
+ rc = ::setsockopt( m_socket, SOL_IP, IP_ROUTER_ALERT, &val,
+ sizeof(val) );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(IP_ROUTER_ALERT)");
+ return false;
+ }
+ */
+
+#ifdef WIN32
+ //----------------------
+ // Bind the socket.
+ // WIN32 requires this on a specific interface!
+ // INADDR_ANY is not allowed according to MSDN Lib for raw IGMP sockets.
+ sockaddr_in m_LocalAddr;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = 0;
+ m_LocalAddr.sin_addr.s_addr = m_bindaddr;
+
+ rc = ::bind( m_socket, (struct sockaddr*)&m_LocalAddr,
+ sizeof(m_LocalAddr) );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP bind()");
+ return false;
+ }
+
+ int len = sizeof(struct sockaddr_in);
+ rc = ::getsockname( m_socket, (struct sockaddr*)&m_LocalAddr,
+ (socklen_t*) &len );
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP getsockname()");
+ return false;
+ }
+#else
+#ifndef APPLE
+ // Normal bind() for this socket does not really work. the socket
+ // does not receive anything then.
+ // However, SO_BINDTODEVICE _does_ work. Maybe because of SOCK_RAW?!
+ rc = setsockopt(m_socket, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *)&bindif.name, sizeof(bindif.name));
+ if ( rc < 0 )
+ {
+ log_socket_error("IGMP setsockopt(SO_BINDTODEVICE)");
+ return false;
+ }
+#endif
+#endif
+
+#ifdef WIN32
+ DWORD dwCtlCode = SIO_RCVALL_IGMPMCAST;
+ DWORD dwBytesSent = 0;
+ rc = WSAIoctl(m_socket, dwCtlCode, &dwCtlCode, sizeof(dwCtlCode), NULL,
+ 0, &dwBytesSent, NULL, NULL);
+ if ( rc == SOCKET_ERROR )
+ {
+ log_socket_error("IGMP WSAIoctl()");
+ return false;
+ }
+#endif
+
+
+ if ( Membership(IGMP_ALL_HOSTS, true)
+ && Membership(IGMP_ALL_V3REPORTS, true)
+ && Membership(IGMP_ALL_ROUTER, true))
+ {
+ Start();
+ return true;
+ }
+
+ return false;
+}
+
+void cIgmpListener::Action()
+{
+ int recvlen;
+ struct pollfd p;
+ char recv_buf[8192];
+
+
+ p.fd = m_socket;
+ p.events = POLLIN;
+
+ while (Running())
+ {
+ switch (poll(&p,1,1000)) {
+ case -1:
+#ifndef WIN32
+ if ( (errno != EINTR) && (errno != EWOULDBLOCK) )
+#else
+ int Rt;
+ Rt = WSAGetLastError();
+ if ( (Rt != WSAEINTR) && (Rt != WSAEWOULDBLOCK) )
+#endif
+ log_socket_error( "IGMP poll()" );
+ case 0:
+ continue;
+
+ default:
+ if (!(p.revents&POLLIN))
+ continue;
+
+ recvlen = recv(m_socket, (char*)&recv_buf,
+ sizeof(recv_buf), 0);
+ if (recvlen < 0)
+ {
+#ifndef WIN32
+ if ( (errno == EINTR) || (errno == EWOULDBLOCK) )
+#else
+ int Rt;
+ Rt = WSAGetLastError();
+ if ( (Rt == WSAEINTR) || (Rt == WSAEWOULDBLOCK) )
+#endif
+ continue;
+ log_socket_error("IGMP recv()");
+ goto out;
+ }
+ Parse(recv_buf, recvlen);
+ break;
+ }
+ }
+out:;
+}
+
+void cIgmpListener::Parse(char* buffer, int len)
+{
+ char* buf = buffer;
+ in_addr_t groupaddr;
+ in_addr_t senderaddr;
+
+ // Make sure we can at least extract the IP version
+ if (len < (int) sizeof(char))
+ {
+ return;
+ }
+ // Check that IP version is IPV4 and that payload is IGMP
+ if ( (HI_BYTE(*buf) == 4) && ( *(buf+9) == IPPROTO_IGMP) )
+ {
+ if (!quiet) {
+ printf("IGMP: SrcAddr: %d.%d.%d.%d -> ",
+ (unsigned char) *(buf+12),
+ (unsigned char) *(buf+13),
+ (unsigned char) *(buf+14),
+ (unsigned char) *(buf+15) );
+ printf("DstAddr: %d.%d.%d.%d\n",
+ (unsigned char) *(buf+16),
+ (unsigned char) *(buf+17),
+ (unsigned char) *(buf+18),
+ (unsigned char) *(buf+19) );
+ }
+ memcpy(&senderaddr, buf+12, 4);
+
+ // skip rest of ip header and move to next to
+ // next protocol header
+ len -= LO_BYTE(*buf) * 4;
+ buf += LO_BYTE(*buf) * 4;
+
+ uint16_t chksum = ((*(buf+3)<<8)&0xFF00 ) | (*(buf+2) & 0x00FF);
+ *(buf+2) = 0;
+ *(buf+3) = 0;
+ if (chksum != inetChecksum((uint16_t *)buf, len))
+ {
+ printf("IGMP: INVALID CHECKSUM 0x%04x 0x%04x - "
+ "discarding packet.\n", chksum,
+ inetChecksum((uint16_t *)buf, len));
+ return;
+ }
+
+ if ( (len == 8) )
+ {
+ if ( *buf == 0x11 )
+ {
+ if (!quiet) {
+ printf("IGMP: Version: %s, Type: "
+ "Membership Query\n",
+ (*(buf+1) == 0 ) ? "1":"2");
+ printf(" Group: %d.%d.%d.%d\n",
+ (unsigned char) *(buf+4),
+ (unsigned char) *(buf+5),
+ (unsigned char) *(buf+6),
+ (unsigned char) *(buf+7));
+ }
+ memcpy(&groupaddr, buf+4, 4);
+ m_IgmpMain->ProcessIgmpQueryMessage( groupaddr,
+ senderaddr );
+ }
+ else if ( *buf == 0x12 )
+ {
+ if (!quiet)
+ printf("IGMP: Version: 1, Type: "
+ "Membership Report\n");
+ }
+ else if (*buf == 0x16)
+ {
+ if (!quiet)
+ printf("IGMP: Version: 2, Type: "
+ "Membership Report\n");
+ }
+ else if (*buf == 0x17)
+ {
+ if (!quiet)
+ printf("IGMP: Version: 2, Type: "
+ "Leave Group\n");
+ }
+
+ if( !quiet)
+ printf(" Group: %d.%d.%d.%d\n",
+ (unsigned char) *(buf+4),
+ (unsigned char) *(buf+5),
+ (unsigned char) *(buf+6),
+ (unsigned char) *(buf+7));
+ memcpy(&groupaddr, buf+4, 4);
+ m_IgmpMain->ProcessIgmpReportMessage( (int) *buf,
+ groupaddr, senderaddr);
+ }
+ else if ( (*buf == 0x11) && (len > 8) )
+ {
+ if (!quiet) {
+ printf("IGMP: Version: 3, Type: "
+ "Membership Query, Maximum "
+ "Response Time: %d\n",*(buf+1));
+ printf(" Group: %d.%d.%d.%d\n",
+ (unsigned char) *(buf+4),
+ (unsigned char) *(buf+5),
+ (unsigned char) *(buf+6),
+ (unsigned char) *(buf+7));
+ }
+ memcpy(&groupaddr, buf+4, 4);
+ m_IgmpMain->ProcessIgmpQueryMessage( groupaddr,
+ senderaddr );
+
+ }
+ else if ( (*buf == 0x22) && (len > 8) )
+ {
+ unsigned short numrecords = ntohs((unsigned short)*
+ (buf+7)<<8|*(buf+6));
+ if (!quiet)
+ printf("IGMP: Version: 3, Type: Membership "
+ "Report, Number of Records: %d\n",
+ numrecords);
+
+ // Skip Header and move to records
+ buf += 8;
+ for(int i = 0; i<numrecords; i++)
+ {
+ unsigned short numsources = (unsigned short)*
+ (buf+3)<<8|*(buf+2);
+ if (!quiet) {
+ printf(" ---> Record No: %d, Type: "
+ "%d, Number of Sources: %d\n",
+ i+1, *buf, numsources);
+ printf(" Group: %d.%d.%d.%d\n",
+ (unsigned char) *(buf+4),
+ (unsigned char) *(buf+5),
+ (unsigned char) *(buf+6),
+ (unsigned char) *(buf+7));
+ }
+ memcpy(&groupaddr, buf+4, 4);
+ m_IgmpMain->ProcessIgmpReportMessage(
+ (int) *buf, groupaddr, senderaddr);
+
+ if (!quiet) for(int j = 0; j<numsources; j++)
+ {
+ printf(" Sources: "
+ "%d.%d.%d.%d\n",
+ (unsigned char) *(buf+j*4),
+ (unsigned char) *(buf+j*4+1),
+ (unsigned char) *(buf+j*4+2),
+ (unsigned char) *(buf+j*4+3));
+ // move to next record: header bytes +
+ // aux data len
+ }
+ buf += 8 + *(buf+1) + (numsources *4);
+ }
+ }
+ else
+ {
+ printf("IGMP: Invalid packet\n");
+ }
+ }
+
+}
+
+void cIgmpListener::IGMPSendQuery(in_addr_t Group, int Timeout)
+{
+ struct sockaddr_in dst;
+ uint16_t query[6];
+
+ dst.sin_family = AF_INET;
+ dst.sin_port = IPPROTO_IGMP;
+ dst.sin_addr.s_addr = Group;
+
+ query[0] = ((Timeout * 10)&0xFF)<<8 | 0x11 ; // Membership Query
+ query[1] = 0x0000; // Checksum
+ if ( Group == IGMP_ALL_HOSTS )
+ { // General Query
+ query[2] = 0x0000;
+ query[3] = 0x0000;
+ }
+ else
+ { // Group Query
+ memcpy(&(query[2]), &Group, 4);
+ }
+ query[4] = IGMP_QUERY_INTERVAL<<8 | 0x00 ; // S=0, QRV=0
+ query[5] = 0x0000; // 0 sources
+ uint16_t chksum = inetChecksum( (uint16_t *) &query, sizeof(query));
+ query[1] = chksum;
+
+ for (int i = 0; i < 5 && ::sendto(m_socket, (const char*) &query,
+ sizeof(query), 0, (sockaddr*)&dst, sizeof(dst)) < 0 ; i++)
+ {
+#ifndef WIN32
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+#else
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+#endif
+ {
+ printf("IGMP: unable to send query for group %s:",
+ inet_ntoa(dst.sin_addr));
+ log_socket_error("IGMP sendto()");
+ break;
+ }
+
+ cCondWait::SleepMs(10);
+ }
+}
+
+
+// -------------------------------------------------------------------------------------------------------------------------
+
+cIgmpMain::cIgmpMain(cStreamer* streamer, iface_t bindif, int table)
+ : cThread("IGMP timeout handler")
+{
+ m_bindaddr = bindif.ipaddr;
+ m_bindif = bindif;
+ m_table = table;
+
+ m_IgmpListener = new cIgmpListener(this);
+ m_StartupQueryCount = IGMP_STARTUP_QUERY_COUNT;
+ m_Querier = true;
+ m_streamer = streamer;
+ m_stopping = 0;
+ TV_CLR(m_GeneralQueryTimer);
+}
+
+cIgmpMain::~cIgmpMain(void)
+{
+}
+
+void cIgmpMain::Destruct(void)
+{
+ if (m_IgmpListener)
+ {
+ m_IgmpListener->Destruct();
+ }
+
+ // Wake up timeout thread in case it is currently sleeping
+ m_stopping=1;
+ m_CondWait.Signal();
+
+ // Now try to stop thread. After 3 seconds it will be canceled.
+ Cancel(3);
+
+ cMulticastGroup *del = NULL;
+
+ for (cMulticastGroup *group = m_Groups.First(); group;
+ group = m_Groups.Next(group))
+ {
+ if ( group->stream )
+ {
+ m_streamer->StopMulticast(group);
+ }
+ if (del)
+ m_Groups.Del(del);
+ del = group;
+ }
+ if (del)
+ m_Groups.Del(del);
+
+}
+
+bool cIgmpMain::StartListener(void)
+{
+ if ( m_IgmpListener && m_IgmpListener->Initialize(m_bindif, m_table) )
+ {
+ Start();
+ return true;
+ }
+ else
+ {
+ printf("cIgmpMain: Cannot create IGMP listener.\n");
+ return false;
+ }
+}
+
+void cIgmpMain::ProcessIgmpQueryMessage(in_addr_t groupaddr,
+ in_addr_t senderaddr)
+{
+ if (ntohl(senderaddr) < ntohl(m_bindaddr))
+ {
+ in_addr sender;
+ sender.s_addr = senderaddr;
+ printf("IGMP: Another Querier with lower IP address (%s) "
+ "is active.\n", inet_ntoa(sender));
+ IGMPStartOtherQuerierPresentTimer();
+ }
+}
+
+void cIgmpMain::ProcessIgmpReportMessage(int type, in_addr_t groupaddr,
+ in_addr_t senderaddr)
+{
+ cMulticastGroup* group;
+
+ if ( !m_streamer->IsGroupinRange(groupaddr) )
+ return;
+
+ LOCK_THREAD;
+
+ switch (type)
+ {
+ case IGMPV3_MR_MODE_IS_INCLUDE:
+ break;
+
+ case IGMPV1_MEMBERSHIP_REPORT:
+ case IGMPV2_MEMBERSHIP_REPORT:
+ case IGMPV3_MR_MODE_IS_EXCLUDE:
+ case IGMPV3_MR_CHANGE_TO_EXCLUDE:
+ group = FindGroup(groupaddr);
+ if (!group)
+ {
+ group = new cMulticastGroup(groupaddr);
+ m_Groups.Add(group);
+ }
+ if (!group->stream)
+ {
+ m_streamer->StartMulticast(group);
+ }
+ IGMPStartTimer(group, senderaddr);
+ if (type == IGMPV1_MEMBERSHIP_REPORT)
+ IGMPStartV1HostTimer(group);
+ break;
+
+ case IGMPV2_LEAVE_GROUP:
+ case IGMPV3_MR_CHANGE_TO_INCLUDE:
+ group = FindGroup(groupaddr);
+ if (group && !TV_SET(group->v1timer))
+ {
+ if (group->reporter == senderaddr)
+ {
+ IGMPStartTimerAfterLeave(group, m_Querier ?
+ IGMP_LAST_MEMBER_QUERY_INTERVAL_TS : 1);
+ //Igmp->igmp_code);
+ if (m_Querier)
+ IGMPSendGroupQuery(group);
+ IGMPStartRetransmitTimer(group);
+ }
+ m_CondWait.Signal();
+ }
+ break;
+
+ case IGMPV3_MR_ALLOW_NEW_SOURCES:
+ printf("IGMPv3 Group Record Type \"ALLOW_NEW_SOURCES\" "
+ "ignored.\n");
+ break;
+ case IGMPV3_MR_BLOCK_OLD_SOURCES:
+ printf("IGMPv3 Group Record Type \"BLOCK_OLD_SOURCES\" "
+ "ignored.\n");
+ break;
+ default:
+ printf("Unknown IGMPv3 Group Record Type.\n");
+ }
+}
+
+void cIgmpMain::Action()
+{
+ while (Running())
+ {
+ struct timeval now;
+ struct timeval next;
+
+ gettimeofday(&now, NULL);
+ TV_CPY(next, now);
+ next.tv_sec += IGMP_QUERY_INTERVAL;
+
+ cMulticastGroup *del = NULL;
+
+ {// <-- ThreadLock is locked only within this block!
+ LOCK_THREAD;
+
+ if (TV_CMP(m_GeneralQueryTimer, <, now)) {
+ if (!quiet)
+ printf("IGMP: Starting General Query.\n");
+ IGMPSendGeneralQuery();
+ IGMPStartGeneralQueryTimer();
+ }
+ if (TV_CMP(next, >, m_GeneralQueryTimer))
+ TV_CPY(next, m_GeneralQueryTimer);
+
+ for (cMulticastGroup *group = m_Groups.First(); group;
+ group = m_Groups.Next(group))
+ {
+ if (TV_CMP(group->timeout, <, now))
+ {
+ m_streamer->StopMulticast(group);
+ IGMPClearRetransmitTimer(group);
+ if (del)
+ m_Groups.Del(del);
+ del = group;
+ }
+ else if (m_Querier && TV_SET(group->retransmit)
+ && TV_CMP(group->retransmit, <, now)) {
+ IGMPSendGroupQuery(group);
+ IGMPStartRetransmitTimer(group);
+ if (TV_CMP(next, >,
+ group->retransmit))
+ TV_CPY(next,
+ group->retransmit);
+ }
+ else if (TV_SET(group->v1timer) &&
+ TV_CMP(group->v1timer, <, now)) {
+ TV_CLR(group->v1timer);
+ }
+ else {
+ if (TV_CMP(next, >, group->timeout))
+ TV_CPY(next, group->timeout);
+ if (TV_SET(group->retransmit) &&
+ TV_CMP(next, >,
+ group->retransmit))
+ TV_CPY(next, group->retransmit);
+ if (TV_SET(group->v1timer) &&
+ TV_CMP(next, >, group->v1timer))
+ TV_CPY(next, group->v1timer);
+ }
+ }
+ if (del)
+ m_Groups.Del(del);
+ }
+
+ int sleep = (next.tv_sec - now.tv_sec) * 1000;
+ sleep += (next.tv_usec - now.tv_usec) / 1000;
+ if (next.tv_usec < now.tv_usec)
+ sleep += 1000;
+ if (m_stopping)
+ sleep = 100;
+ else if (!sleep)
+ continue;
+ if (!quiet)
+ printf("IGMP main thread: Sleeping %d ms\n", sleep);
+ m_CondWait.Wait(sleep);
+ }
+}
+
+cMulticastGroup* cIgmpMain::FindGroup(in_addr_t Group) const
+{
+ cMulticastGroup *group = m_Groups.First();
+ while (group && group->group != Group)
+ group = m_Groups.Next(group);
+ return group;
+}
+
+void cIgmpMain::IGMPStartGeneralQueryTimer()
+{
+ m_Querier = true;
+ if (m_StartupQueryCount) {
+ gettimeofday(&m_GeneralQueryTimer, NULL);
+ m_GeneralQueryTimer.tv_sec += IGMP_STARTUP_QUERY_INTERVAL;
+ m_StartupQueryCount--;
+ }
+ else {
+ gettimeofday(&m_GeneralQueryTimer, NULL);
+ m_GeneralQueryTimer.tv_sec += IGMP_QUERY_INTERVAL;
+ }
+}
+
+void cIgmpMain::IGMPStartOtherQuerierPresentTimer()
+{
+ m_Querier = false;
+ m_StartupQueryCount = 0;
+ gettimeofday(&m_GeneralQueryTimer, NULL);
+ m_GeneralQueryTimer.tv_sec += IGMP_OTHER_QUERIER_PRESENT_INTERVAL;
+}
+
+void cIgmpMain::IGMPSendGeneralQuery()
+{
+ m_IgmpListener->IGMPSendQuery(IGMP_ALL_HOSTS,
+ IGMP_QUERY_RESPONSE_INTERVAL);
+}
+
+void cIgmpMain::IGMPStartRetransmitTimer(cMulticastGroup* Group)
+{
+ gettimeofday(&Group->retransmit, NULL);
+ TV_ADD(Group->retransmit, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
+}
+
+void cIgmpMain::IGMPClearRetransmitTimer(cMulticastGroup* Group)
+{
+ TV_CLR(Group->retransmit);
+}
+
+// Group state actions
+void cIgmpMain::IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member)
+{
+ gettimeofday(&Group->timeout, NULL);
+ Group->timeout.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ TV_CLR(Group->retransmit);
+ Group->reporter = Member;
+
+}
+
+void cIgmpMain::IGMPStartV1HostTimer(cMulticastGroup* Group)
+{
+ gettimeofday(&Group->v1timer, NULL);
+ Group->v1timer.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL;
+}
+
+void cIgmpMain::IGMPStartTimerAfterLeave(cMulticastGroup* Group,
+ unsigned int MaxResponseTimeTs)
+{
+ //Group->Update(time(NULL) + MaxResponseTime
+ //* IGMP_LAST_MEMBER_QUERY_COUNT / 10);
+ MaxResponseTimeTs *= IGMP_LAST_MEMBER_QUERY_COUNT;
+ gettimeofday(&Group->timeout, NULL);
+ TV_ADD(Group->timeout, MaxResponseTimeTs);
+ TV_CLR(Group->retransmit);
+ Group->reporter = 0;
+}
+
+void cIgmpMain::IGMPSendGroupQuery(cMulticastGroup* Group)
+{
+ m_IgmpListener->IGMPSendQuery(Group->group,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL_TS);
+}
+
diff --git a/mcast/netcv2dvbip/igmp.h b/mcast/netcv2dvbip/igmp.h
new file mode 100644
index 0000000..9032e83
--- /dev/null
+++ b/mcast/netcv2dvbip/igmp.h
@@ -0,0 +1,123 @@
+#ifndef __IGMP_H
+#define __IGMP_H
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#include "clist.h"
+#include "thread.h"
+#include "misc.h"
+#include "iface.h"
+
+#define IGMPV1_MEMBERSHIP_REPORT 0x12 /* Ver. 1 membership report */
+#define IGMPV2_MEMBERSHIP_REPORT 0x16 /* Ver. 2 membership report */
+#define IGMPV2_LEAVE_GROUP 0x17 /* Leave-group message */
+
+#define IGMPV3_MR_MODE_IS_INCLUDE 0x01
+#define IGMPV3_MR_MODE_IS_EXCLUDE 0x02
+#define IGMPV3_MR_CHANGE_TO_INCLUDE 0x03
+#define IGMPV3_MR_CHANGE_TO_EXCLUDE 0x04
+#define IGMPV3_MR_ALLOW_NEW_SOURCES 0x05
+#define IGMPV3_MR_BLOCK_OLD_SOURCES 0x06
+
+class cStream;
+class cStreamer;
+
+class cMulticastGroup : public cListObject
+{
+ public:
+ cMulticastGroup(in_addr_t Group);
+ in_addr_t group;
+ in_addr_t reporter;
+ struct timeval timeout;
+ struct timeval v1timer;
+ struct timeval retransmit;
+ cStream* stream;
+};
+
+class cIgmpMain;
+
+class cIgmpListener : public cThread
+{
+ public:
+ cIgmpListener(cIgmpMain* igmpmain);
+
+ bool Initialize(iface_t bindif, int table);
+ void Destruct(void);
+ bool Membership(in_addr_t mcaddr, bool Add);
+ void IGMPSendQuery(in_addr_t Group, int Timeout);
+
+ private:
+ int m_socket;
+ in_addr_t m_bindaddr;
+
+ cIgmpMain* m_IgmpMain;
+
+ void Parse(char*, int);
+
+ virtual void Action();
+
+};
+
+//-------------------------------------------------------------------------------------------------------------------
+
+class cIgmpMain : public cThread
+{
+ public:
+ cIgmpMain(cStreamer* streamer, iface_t bindif, int table);
+ ~cIgmpMain(void);
+ bool StartListener(void);
+ void Destruct(void);
+ void ProcessIgmpQueryMessage(in_addr_t Group, in_addr_t Sender);
+ void ProcessIgmpReportMessage(int type, in_addr_t Group,
+ in_addr_t Sender);
+
+ private:
+ // Parent streamer
+ cStreamer* m_streamer;
+
+ // Listener
+ cIgmpListener* m_IgmpListener;
+ in_addr_t m_bindaddr;
+ iface_t m_bindif;
+ int m_table;
+
+ cList<cMulticastGroup> m_Groups;
+
+ // General Query / Timer
+ struct timeval m_GeneralQueryTimer;
+ bool m_Querier;
+ int m_StartupQueryCount;
+
+ void IGMPStartGeneralQueryTimer();
+ void IGMPStartOtherQuerierPresentTimer();
+ void IGMPSendGeneralQuery();
+
+ // Group Query / Timer
+ void IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member);
+ void IGMPStartV1HostTimer(cMulticastGroup* Group);
+ void IGMPStartTimerAfterLeave(cMulticastGroup* Group,
+ unsigned int MaxResponseTime);
+ void IGMPStartRetransmitTimer(cMulticastGroup* Group);
+ void IGMPClearRetransmitTimer(cMulticastGroup* Group);
+ void IGMPSendGroupQuery(cMulticastGroup* Group);
+ void IGMPStartMulticast(cMulticastGroup* Group);
+ void IGMPStopMulticast(cMulticastGroup* Group);
+
+ // Main thread
+ virtual void Action();
+ cCondWait m_CondWait;
+ int m_stopping;
+
+ // Helper
+ cMulticastGroup* FindGroup(in_addr_t Group) const;
+ bool IsGroupinRange(in_addr_t groupaddr);
+};
+
+#endif
diff --git a/mcast/netcv2dvbip/main.c b/mcast/netcv2dvbip/main.c
new file mode 100644
index 0000000..3b31406
--- /dev/null
+++ b/mcast/netcv2dvbip/main.c
@@ -0,0 +1,400 @@
+#include "dvbipstream.h"
+#include "iface.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifndef WIN32
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#ifndef APPLE
+#define _LINUX_IN_H
+#include <linux/mroute.h>
+#endif
+#else
+#include <direct.h>
+#ifdef __MINGW32__
+#include <getopt.h>
+#endif
+#endif
+
+#include "streamer.h"
+
+#define PORT_MAX 6
+
+#ifndef WIN32
+// Local function Prototypes
+static void signalHandler(int);
+
+// Global vars...
+static int sighandled = 0;
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGTERM 0x04
+#endif
+
+channel_t *channels=NULL;
+int channel_num=0;
+int channel_max_num=0;
+int channel_use_eit = 0;
+int channel_use_sdt = 0;
+int portnum = 12345;
+int table = 0;
+int quiet = 0;
+
+/*-------------------------------------------------------------------------*/
+channel_t * read_channel_list(char *filename, char *dirname)
+{
+ FILE *cf;
+ FILE *pf;
+ char buf[512];
+
+ cf=fopen(filename,"r");
+ if (!cf) {
+ printf("Can't read %s: %s\n",filename,strerror(errno));
+ return NULL;
+ }
+
+ if (dirname) {
+#ifdef WIN32
+ if (((dirname[0]>='A'&&dirname[0]<='Z') ||
+ (dirname[0]>='a'&&dirname[0]<='z')) && dirname[1]==':') {
+ if (_chdrive((dirname[0]&0xdf)-'A'+1)) {
+ printf("Can't access %s: %s\n", dirname,
+ strerror(errno));
+ fclose(cf);
+ return NULL;
+ }
+ }
+#endif
+ if (chdir(dirname)) {
+ printf("Can't access %s: %s\n", dirname,
+ strerror(errno));
+ fclose(cf);
+ return NULL;
+ }
+ }
+
+ pf =fopen("channels.m3u", "w");
+ if (!pf) {
+ printf("Can't create %s: %s\n", "channels.m3u",
+ strerror(errno));
+ fclose(cf);
+ return NULL;
+ }
+ fprintf(pf, "#EXTM3U\n");
+
+ while(fgets(buf,sizeof(buf),cf)) {
+ if (channel_num==channel_max_num) {
+ channel_max_num+=200;
+ channels=(channel_t*)realloc(channels,
+ channel_max_num*sizeof(channel_t));
+ if (!channels) {
+ printf("out of memory\n");
+ fclose(pf);
+ fclose(cf);
+ return NULL;
+ }
+ }
+ if (ParseLine(buf,&channels[channel_num])) {
+ int ip1 = (channel_num+1)/256;
+ int ip2 = (channel_num) - (ip1*256) + 1;
+ if (!quiet)
+ printf("%i: udp://@239.255.%i.%i:%i - %s \n",
+ channel_num+1, ip1, ip2, portnum,
+ channels[channel_num].name);
+ fprintf(pf, "#EXTINF: %i,%s\n", channel_num+1,
+ channels[channel_num].name);
+ fprintf(pf, "udp://@239.255.%i.%i:%i\n", ip1,ip2,
+ portnum);
+ if (channel_use_eit)
+ {
+ channels[channel_num].NumEitpids = 1;
+ channels[channel_num].eitpids[0] = 0x12;
+ }
+ if (channel_use_sdt)
+ {
+ channels[channel_num].NumSdtpids = 1;
+ channels[channel_num].sdtpids[0] = 0x11;
+ }
+ channel_num++;
+ }
+ }
+ printf("Read %i channels, M3U playlist file \"channels.m3u\" "
+ "generated.\n",channel_num);
+ fclose(pf);
+ fclose(cf);
+ return channels;
+}
+/*-------------------------------------------------------------------------*/
+int get_channel_num(void)
+{
+ return channel_num;
+}
+/*-------------------------------------------------------------------------*/
+int get_channel_name(int n, char *str, int maxlen)
+{
+#ifndef WIN32
+ snprintf(str,maxlen,"%04i_%s.ts",n+1,channels[n].name);
+#else
+ sprintf(str,"%04i_%s.ts",n+1,channels[n].name);
+#endif
+ while(*str) {
+ char c=*str;
+ if (c=='/' || c=='\\' || c==':' || c=='?' || c=='*' ||
+ !isprint(c))
+ *str='_';
+ str++;
+ }
+ return 0;
+}
+/*-------------------------------------------------------------------------*/
+channel_t *get_channel_data(int n)
+{
+ return &channels[n];
+}
+
+
+extern cmdline_t cmd;
+
+void usage (void)
+{
+ printf("Usage: netcv2dvbip "
+ "[-b <multicast interface>] "
+ "[-p <port>] "
+ "[-i <netceiver interface>] "
+ "[-c <channels.conf>] "
+ "[-e activate EIT PID (EPG)] "
+ "[-s activate SDT PID (may be required for EPG)] "
+#ifdef MRT_TABLE
+ "[-t <routing table number> (requires "
+ "CONFIG_IP_MROUTE_MULTIPLE_TABLES)] "
+#endif
+ "[-q be more quiet on the screen] "
+ "[-o <output directory>]\n");
+ exit(1);
+}
+
+/*-------------------------------------------------------------------------*/
+int main(int argc, char *argv[])
+{
+ char c;
+ char *dirname = NULL;
+ char channels[_POSIX_PATH_MAX];
+ strcpy(channels, "channels.conf");
+ char bindiface[IFNAMSIZ];
+ iface_t iflist[MAXIF];
+
+#ifndef WIN32
+ uid_t uid;
+ struct sigaction sa;
+
+ uid = getuid();
+ if(uid != 0)
+ {
+ printf("You must be root! Current uid: %d.\n",uid);
+ return(1);
+ }
+
+ sa.sa_handler = signalHandler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+#else
+ bool IsAdmin = false;
+ IsUserAdmin(&IsAdmin);
+ if( IsAdmin == false)
+ {
+ printf("You must have administrative privileges!\n");
+ return(1);
+ }
+#endif
+
+ memset(bindiface, 0, sizeof(bindiface));
+
+ while(1)
+ {
+ int ret = getopt(argc,argv, "i:hc:b:p:esqo:"
+#ifdef MRT_TABLE
+ "t:"
+#endif
+ );
+ if (ret==-1)
+ break;
+
+ c=(char)ret;
+
+ switch (c) {
+ case 'i':
+ strncpy(cmd.iface, optarg, IFNAMSIZ-1);
+ cmd.iface[IFNAMSIZ-1]=0;
+ break;
+ case 'c':
+ strncpy(channels, optarg, _POSIX_PATH_MAX-1);
+ channels[_POSIX_PATH_MAX-1]=0;
+ break;
+ case 'b':
+ strncpy(bindiface, optarg, IFNAMSIZ-1);
+ bindiface[IFNAMSIZ-1] = 0;
+ break;
+ case 'p':
+ portnum = atoi(optarg);
+ break;
+#ifdef MRT_TABLE
+ case 't':
+ table = atoi(optarg);
+ break;
+#endif
+ case 'h':
+ usage();
+ return(0);
+ case 'e':
+ channel_use_eit = 1;
+ break;
+ case 's':
+ channel_use_sdt = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'o':
+ dirname = optarg;
+ break;
+ }
+ }
+
+ memset( iflist, 0, sizeof(iflist));
+ int num_ifaces=discover_interfaces( (iface_t*) &iflist );
+ if ( !num_ifaces )
+ exit(-1);
+
+ int ifindex = 0;
+ if ( strlen(bindiface) > 0 )
+ {
+ int found = false;
+ while ( ifindex < num_ifaces )
+ {
+ if (!strncmp(iflist[ifindex].name, bindiface, IFNAMSIZ))
+ {
+ found = true;
+ break;
+ }
+ ifindex++;
+ }
+ if (!found)
+ {
+ printf("Cannot find interface %s. Exiting...\n",
+ bindiface);
+ exit(-1);
+ }
+
+ }
+
+ printf("Starting netcv2dvbip. Streams will be sent to port: %d\n",
+ portnum);
+
+ if (!read_channel_list(channels, dirname))
+ exit(-1);
+
+ mcli_startup();
+
+#ifdef WIN32
+ if ( !IsVistaOrHigher() )
+ {
+ printf("Windows version does not support MLDv2, enabling "
+ "internal MLDv2 reporter.\n");
+ mld_client_init(cmd.iface);
+ }
+#endif
+
+ printf("\n");
+
+#ifdef WIN32
+ WSADATA ws;
+ WSAStartup(MAKEWORD (2, 2),&ws);
+#endif
+
+ cStreamer streamer;
+
+ struct sockaddr_in listenaddr;
+ listenaddr.sin_addr.s_addr = iflist[ifindex].ipaddr;
+ printf("Listening on interface %s (%s)\n\n\n", iflist[ifindex].name,
+ inet_ntoa(listenaddr.sin_addr));
+
+ streamer.SetBindIf(iflist[ifindex]);
+ streamer.SetStreamPort(portnum);
+ streamer.SetTable(table);
+ streamer.SetNumGroups(get_channel_num());
+
+ streamer.Run();
+
+#ifdef WIN32
+ printf("\nPlease press enter to stop.\n\n\n");
+ getchar();
+ printf("Stopping...please wait...\n");
+#else
+ while (1)
+ {
+ // Process signaling...
+ if (sighandled) {
+ if (sighandled & GOT_SIGINT) {
+ sighandled &= ~GOT_SIGINT;
+ printf("\nGot SIGINT signal. Exiting.\n");
+ break;
+ }
+ if (sighandled & GOT_SIGTERM) {
+ sighandled &= ~GOT_SIGTERM;
+ printf("Got SIGTERM signal. Exiting.\n");
+ break;
+ }
+ }
+ usleep(10000);
+ }
+#endif
+
+ streamer.Stop();
+#ifdef WIN32
+ if ( !IsVistaOrHigher() )
+ {
+ mld_client_exit();
+ }
+#endif
+ printf("netcv2dvbip stopped.\n");
+
+#ifdef WIN32
+ WSACleanup();
+#endif
+ return 0;
+}
+/*-------------------------------------------------------------------------*/
+#ifndef WIN32
+/*
+ * Signal handler. Take note of the fact that the signal arrived
+ * so that the main loop can take care of it.
+ */
+static void signalHandler(int sig) {
+ switch (sig) {
+ case SIGINT:
+ sighandled |= GOT_SIGINT;
+ break;
+ case SIGTERM:
+ sighandled |= GOT_SIGTERM;
+ break;
+ /* XXX: Not in use.
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+ */
+ }
+}
+#endif
diff --git a/mcast/netcv2dvbip/makefile b/mcast/netcv2dvbip/makefile
new file mode 100644
index 0000000..75ff51c
--- /dev/null
+++ b/mcast/netcv2dvbip/makefile
@@ -0,0 +1,61 @@
+CC=g++
+CFLAGS=-O3
+LDFLAGS=-s
+
+ARCH= $(shell $(CC) -dumpmachine)
+APPLE_DARWIN = $(shell echo $(ARCH) | grep -q 'apple-darwin' && echo "1" || echo "0")
+CYGWIN = $(shell echo $(ARCH) | grep -q 'cygwin' && echo "1" || echo "0")
+MIPSEL = $(shell echo $(ARCH) | grep -q 'mipsel' && echo "1" || echo "0")
+
+ifeq ($(APPLE_DARWIN), 1)
+DEFS:=$(DEFS) -I../common/darwin/include/ -DAPPLE
+APPLE=1
+CFLAGS:= $(CFLAGS) -fno-common -Wall
+else
+CFLAGS:= $(CFLAGS) -Wall -Woverloaded-virtual
+endif
+
+ifeq ($(MIPSEL),1)
+DEFS:=$(DEFS) -DMIPSEL
+XML_LIB:=-lxml2
+else
+XML_INC:=`xml2-config --cflags`
+XML_LIB:=`xml2-config --libs`
+endif
+
+INCLUDES:=$(INCLUDES) -I../client -I../common $(XML_INC)
+DEFS:=$(DEFS) -g -DCLIENT
+LDADD:=$(LDADD) -L../client
+STATICLIBS:=$(LIBS) ../client/libmcli.a $(XML_LIB) -lpthread
+LIBS:=$(LIBS) $(XML_LIB) -lpthread -lmcli
+LDFLAGS:=$(LDFLAGS) -Wl,--as-needed
+
+netcv2dvbip_OBJECTS=main.o parse.o mclilink.o siparser.o crc32.o clist.o stream.o thread.o misc.o streamer.o igmp.o iface.o
+
+all: netcv2dvbip netcv2dvbip-static
+
+MAKEDEP = $(CC) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): makefile
+ $(MAKEDEP) $(INCLUDES) $(netcv2dvbip_OBJECTS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+netcv2dvbip: $(netcv2dvbip_OBJECTS) ../client/libmcli.so
+ $(CC) $(LDFLAGS) $(netcv2dvbip_OBJECTS) $(LDADD) $(LIBS) -o $@
+
+netcv2dvbip-static: $(netcv2dvbip_OBJECTS) ../client/libmcli.a
+ $(CC) $(LDFLAGS) $(netcv2dvbip_OBJECTS) $(LDADD) $(STATICLIBS) -o $@
+
+../client/libmcli.so: ../client/libmcli.a
+
+../client/libmcli.a:
+ make -C ../client
+
+.c.o:
+ $(CC) $(DEFS) $(INCLUDES) $(CFLAGS) -c $<
+
+
+clean:
+ $(RM) -f $(DEPFILE) *.o *~ netcv2dvbip netcv2dvbip-static
+
diff --git a/mcast/netcv2dvbip/mclilink.c b/mcast/netcv2dvbip/mclilink.c
new file mode 100644
index 0000000..58d22ea
--- /dev/null
+++ b/mcast/netcv2dvbip/mclilink.c
@@ -0,0 +1,659 @@
+#include "dvbipstream.h"
+
+#define BUFFER_SIZE (256*TS_PER_UDP*188)
+
+#ifdef WIN32
+#define CVT (char *)
+#else
+#define CVT
+#endif
+
+int gen_pat(unsigned char *buf, unsigned int program_number,
+ unsigned int pmt_pid, unsigned int ts_cnt)
+{
+ int pointer_field=0;
+ int section_length=13;
+ int transport_stream_id=0;
+ int version_number=0;
+ int current_next_indicator=1;
+ int section_number=0;
+ int last_section_number=0;
+ int i=0;
+ u_long crc;
+
+ buf[i++] = 0x47;
+ buf[i++] = 0x40;
+ buf[i++] = 0x00;
+ buf[i++] = 0x10 | (ts_cnt&0xf);
+
+ buf[i++] = pointer_field;
+ buf[i++] = 0; // table_id
+ buf[i] = 0xB0; // section_syntax_indicator=1, 0, reserved=11
+ buf[i++]|= (section_length>>8)&0x0f;
+ buf[i++] = section_length&0xff;
+
+ buf[i++] = transport_stream_id>>8;
+ buf[i++] = transport_stream_id&0xff;
+
+ buf[i++] = 0xc0 | ((version_number&0x1f) << 1) |
+ (current_next_indicator&1);
+ buf[i++] = section_number;
+ buf[i++] = last_section_number;
+
+ buf[i++] = program_number>>8;
+ buf[i++] = program_number&0xff;
+ buf[i++] = 0xe0 | ((pmt_pid>>8)&0x1F);
+ buf[i++] = pmt_pid&0xff;
+
+ crc=dvb_crc32 ((char *)buf+5, i-5);
+ buf[i++] = (crc>>24)&0xff;
+ buf[i++] = (crc>>16)&0xff;
+ buf[i++] = (crc>>8)&0xff;
+ buf[i++] = crc&0xff;
+
+ for(;i<188;i++) {
+ buf[i] = 0xff;
+ }
+
+// printhex_buf ("PAT", buf, 188);
+ return i;
+}
+
+/*-------------------------------------------------------------------------*/
+int mcli_handle_ts (unsigned char *buffer, size_t len, void *p)
+{
+ stream_info_t *si = (stream_info_t *) p;
+
+ if(si->stop)
+ return len;
+
+ int olen = len;
+
+ int ret;
+ unsigned int i;
+
+again:
+ switch (si->si_state) {
+ case 3:
+ case 0:
+ si->psi.start = 0;
+ si->psi.len = 0;
+ si->si_state++;
+ goto again;
+
+ case 1:
+ ret = 0;
+ for(i=0; i<len; i+=188) {
+ ret = ts2psi_data (buffer+i, &si->psi, 188, 0);
+ if(ret){
+ break;
+ }
+ }
+ if (ret < 0) {
+ si->si_state = 0;
+ }
+
+ if (ret == 1) {
+ if (!quiet)
+ printf ("Channel: %s - Got PAT\n",
+ si->cdata->name);
+ pmt_pid_list_t pat;
+ ret = parse_pat_sect (si->psi.buf, si->psi.len, &pat);
+ if (ret < 0) {
+ si->si_state = 0;
+ } else if (ret == 0) {
+// print_pat (&pat.p, pat.pl, pat.pmt_pids);
+ unsigned int n;
+ for (n = 0; n < pat.pmt_pids; n++) {
+ if (pat.pl[n].program_number ==
+ (unsigned int)si->cdata->sid) {
+ si->pmt_pid =
+ pat.pl[n].network_pmt_pid;
+ if (!quiet)
+ printf ("Channel: %s - SID "
+ "%d has PMT Pid %d\n",
+ si->cdata->name,
+ si->cdata->sid,
+ si->pmt_pid);
+ break;
+ }
+ }
+ if (pat.pmt_pids) {
+ free (pat.pl);
+ }
+ si->si_state++;
+ }
+ }
+ break;
+ case 4:
+ ret = 0;
+ for(i=0; i<len; i+=188) {
+ ret = ts2psi_data(buffer+i, &si->psi, 188, si->pmt_pid);
+ if(ret){
+ break;
+ }
+ }
+ if (ret < 0) {
+ si->si_state = 2;
+ }
+
+ if (ret == 1) {
+ if (!quiet)
+ printf ("Channel: %s - Got PMT\n",
+ si->cdata->name);
+ pmt_t hdr;
+ si_ca_pmt_t pm, es;
+ int es_pid_num;
+// printhex_buf ("Section", si->psi.buf, si->psi.len);
+ si->fta=1;
+ ret = parse_pmt_ca_desc (si->psi.buf, si->psi.len,
+ si->cdata->sid, &pm, &es, &hdr, &si->fta,
+ NULL, &es_pid_num);
+ if (ret < 0) {
+ si->si_state = 2;
+ } else if (ret == 0) {
+ si->es_pidnum = get_pmt_es_pids (es.cad,
+ es.size, si->es_pids, 1);
+ if (si->es_pidnum <= 0) {
+ si->si_state = 2;
+ } else {
+ si->si_state++;
+ }
+ }
+ if (pm.size) {
+ free (pm.cad);
+ }
+ if (es.size) {
+ free (es.cad);
+ }
+ break;
+
+ }
+ case 6:
+ /*
+ for(i=0; i<len; i+=188) {
+ ret = ts2psi_data (buffer+i, &si->psi, 188, 0x12);
+ if(ret==1){
+ printf("Channel: %s - Got EIT\n",
+ si->cdata->name);
+ }
+ }
+ */
+ pthread_mutex_lock(&si->lock_ts);
+
+ switch(len) {
+ case 1*188:
+ case 2*188:
+ case 3*188:
+ case 4*188:
+ case 5*188:
+ case 6*188:
+ case 7*188:
+ break;
+ default:
+ err(CVT "Channel: %s - bad data length %d, skipping\n",
+ si->cdata->name,(int)len);
+ goto out;
+ }
+
+ if(!si->wr) {
+ pthread_mutex_lock(&si->lock_bf);
+ if(si->free) {
+ si->wr=si->free;
+ si->free=si->free->next;
+ pthread_mutex_unlock(&si->lock_bf);
+ } else {
+ pthread_mutex_unlock(&si->lock_bf);
+ si->wr=(stream_buffer_t *)
+ malloc(sizeof(stream_buffer_t));
+ if(!si->wr) {
+ err(CVT "Channel: %s - out of memory\n",
+ si->cdata->name);
+ goto out;
+ }
+ }
+ si->wr->next=NULL;
+ si->fill=0;
+ }
+
+ i=TS_PER_UDP*188-si->fill;
+
+ if(len>i) {
+ memcpy(si->wr->data+si->fill,buffer,i);
+
+ pthread_mutex_lock(&si->lock_bf);
+ if(!si->head)
+ si->head=si->tail=si->wr;
+ else {
+ si->tail->next=si->wr;
+ si->tail=si->wr;
+ }
+ if(si->free) {
+ si->wr=si->free;
+ si->free=si->free->next;
+ pthread_mutex_unlock(&si->lock_bf);
+ } else {
+ pthread_mutex_unlock(&si->lock_bf);
+ si->wr=(stream_buffer_t *)
+ malloc(sizeof(stream_buffer_t));
+ if(!si->wr) {
+ err(CVT "Channel: %s - out of memory\n",
+ si->cdata->name);
+ goto out;
+ }
+ }
+ si->wr->next=NULL;
+ si->fill=0;
+ buffer+=i;
+ len-=i;
+ }
+
+ memcpy(si->wr->data+si->fill,buffer,len);
+ si->fill+=len;
+
+ if(si->fill==TS_PER_UDP*188) {
+ pthread_mutex_lock(&si->lock_bf);
+ if(!si->head)
+ si->head=si->tail=si->wr;
+ else {
+ si->tail->next=si->wr;
+ si->tail=si->wr;
+ }
+ pthread_mutex_unlock(&si->lock_bf);
+ si->wr=NULL;
+ }
+
+out: pthread_mutex_unlock(&si->lock_ts);
+ break;
+ }
+
+ return olen;
+}
+
+/*-------------------------------------------------------------------------*/
+int mcli_handle_ten (tra_t * ten, void *p)
+{
+ if(ten) {
+
+ stream_info_t *si = (stream_info_t *) p;
+ printf("Channel: %s - Status: %02X, Strength: %04X, SNR: %04X,"
+ " BER: %04X\n", si->cdata->name, ten->s.st,
+ ten->s.strength, ten->s.snr, ten->s.ber);
+ }
+ return 0;
+}
+
+#ifdef WIN32THREADS
+DWORD WINAPI stream_watch(__in LPVOID p)
+#else
+void *stream_watch (void *p)
+#endif
+{
+ unsigned char ts[188];
+ stream_info_t *si = (stream_info_t *) p;
+ if (!quiet)
+ printf("Channel: %s - stream watch thread started.\n",
+ si->cdata->name);
+ while (1 ) {
+ if(si->stop)
+ break;
+ if (si->pmt_pid && si->si_state == 2) {
+ dvb_pid_t pids[3];
+ memset (&pids, 0, sizeof (pids));
+ pids[0].pid = si->pmt_pid;
+ pids[1].pid = -1;
+ if (!quiet)
+ printf ("Channel: %s - Add PMT-PID: %d\n",
+ si->cdata->name, si->pmt_pid);
+ recv_pids (si->r, pids);
+ si->si_state++;
+ }
+ if (si->es_pidnum && si->si_state == 5) {
+ int i,k=0;
+ size_t sz = sizeof(dvb_pid_t) * (si->es_pidnum+2 +
+ si->cdata->NumEitpids + si->cdata->NumSdtpids);
+ dvb_pid_t *pids=(dvb_pid_t*)malloc(sz);
+ if(pids==NULL) {
+ err(CVT "Channel: %s - Can't get memory for "
+ "pids\n", si->cdata->name);
+ goto out;
+ }
+ memset (pids, 0, sz);
+ pids[k++].pid = si->pmt_pid;
+ //EIT PIDs
+ for (i = 0; i < si->cdata->NumEitpids; i++)
+ {
+ pids[k++].pid = si->cdata->eitpids[i];
+ if (!quiet)
+ printf("Channel: %s - Add EIT-PID: "
+ "%d\n", si->cdata->name,
+ si->cdata->eitpids[i]);
+ }
+ //SDT PIDs
+ for (i = 0; i < si->cdata->NumSdtpids; i++)
+ {
+ pids[k++].pid = si->cdata->sdtpids[i];
+ if (!quiet)
+ printf("Channel: %s - Add SDT-PID: "
+ "%d\n", si->cdata->name,
+ si->cdata->sdtpids[i]);
+ }
+ for (i = 0; i < si->es_pidnum; i++) {
+ if (!quiet)
+ printf ("Channel: %s - Add ES-PID: "
+ "%d\n", si->cdata->name,
+ si->es_pids[i]);
+ pids[i + k].pid = si->es_pids[i];
+// if(si->cdata->NumCaids) {
+ if(!si->fta) {
+ if (!quiet)
+ printf("Channel: %s - %s\n",
+ si->cdata->name,
+ si->fta ? "Free-To-Air":
+ "Crypted");
+ pids[i + k].id = si->cdata->sid;
+ }
+ pids[i + k +1].pid = -1;
+ }
+ recv_pids (si->r, pids);
+ free(pids);
+ si->si_state++;
+ }
+ if(si->si_state == 6) {
+ gen_pat(ts, si->cdata->sid, si->pmt_pid, si->ts_cnt++);
+ mcli_handle_ts (ts, 188, si);
+ }
+out: usleep (50000);
+ }
+ if (!quiet)
+ printf("Channel: %s - stream watch thread stopped.\n",
+ si->cdata->name);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+void *mcli_stream_setup (const int channum)
+{
+ int cnum;
+ stream_info_t *si;
+ recv_info_t *r;
+ struct dvb_frontend_parameters fep;
+ recv_sec_t sec;
+ dvb_pid_t pids[4];
+ int source = -1;
+ fe_type_t tuner_type = FE_ATSC;
+
+ cnum = channum-1;
+// printf ("mcli_stream_setup %i\n", cnum);
+ if (cnum < 0 || cnum > get_channel_num ())
+ return 0;
+
+ si = (stream_info_t *) malloc (sizeof (stream_info_t));
+ if (!si) {
+ fprintf (stderr, "Cannot get memory for receiver\n");
+ return NULL;
+ }
+ memset(si, 0, sizeof(stream_info_t));
+
+ si->psi.buf = (unsigned char *) malloc (PSI_BUF_SIZE);
+ if (!si->psi.buf) {
+ fprintf (stderr, "Cannot get memory for receiver\n");
+ free (si);
+ return NULL;
+ }
+
+ r = recv_add ();
+ if (!r) {
+ fprintf (stderr, "Cannot get memory for receiver\n");
+ free (si->psi.buf);
+ free (si);
+ return NULL;
+ }
+ si->r = r;
+
+ pthread_mutex_init (&si->lock_bf, NULL);
+ pthread_mutex_init (&si->lock_ts, NULL);
+
+ if (!quiet)
+ register_ten_handler (r, mcli_handle_ten, si);
+ register_ts_handler (r, mcli_handle_ts, si);
+
+ si->cdata = get_channel_data (cnum);
+
+ memset (&fep, 0, sizeof (struct dvb_frontend_parameters));
+ memset (&sec, 0, sizeof (recv_sec_t));
+
+ fep.frequency = si->cdata->frequency;
+ fep.inversion = INVERSION_AUTO;
+ // DVB-S
+ if (si->cdata->source >= 0) {
+ fep.u.qpsk.symbol_rate = si->cdata->srate * 1000;
+ fep.u.qpsk.fec_inner = (fe_code_rate_t)(si->cdata->coderateH |
+ (si->cdata->modulation<<16));
+ fep.frequency *= 1000;
+ fep.inversion = (fe_spectral_inversion_t)si->cdata->inversion;
+
+ sec.voltage = (fe_sec_voltage_t)si->cdata->polarization;
+ sec.mini_cmd = (fe_sec_mini_cmd_t)0;
+ sec.tone_mode = (fe_sec_tone_mode_t)0;
+ tuner_type = FE_QPSK;
+ source = si->cdata->source;
+ }
+ // DVB-T
+ else if (si->cdata->source == -2) {
+ fep.u.ofdm.constellation =
+ (fe_modulation_t)si->cdata->modulation;
+ fep.u.ofdm.code_rate_HP = (fe_code_rate_t)si->cdata->coderateH;
+ fep.u.ofdm.code_rate_LP = (fe_code_rate_t)si->cdata->coderateL;
+ fep.inversion = (fe_spectral_inversion_t)si->cdata->inversion;
+ fep.u.ofdm.bandwidth = (fe_bandwidth_t)si->cdata->bandwidth;
+ fep.u.ofdm.guard_interval =
+ (fe_guard_interval_t)si->cdata->guard;
+ fep.u.ofdm.transmission_mode =
+ (fe_transmit_mode_t)si->cdata->transmission;
+ fep.u.ofdm.hierarchy_information =
+ (fe_hierarchy_t)si->cdata->hierarchy;
+
+ tuner_type = FE_OFDM;
+ source = si->cdata->source;
+ }
+ // DVB-C
+ else if (si->cdata->source == -3) {
+ fep.u.qam.symbol_rate = si->cdata->srate * 1000;
+ fep.u.qam.fec_inner = (fe_code_rate_t)si->cdata->coderateH;
+ fep.u.qam.modulation = (fe_modulation_t)si->cdata->modulation;
+ fep.frequency *= 1000;
+ fep.inversion = (fe_spectral_inversion_t)si->cdata->inversion;
+
+ tuner_type = FE_QAM;
+ source = si->cdata->source;
+ }
+
+
+
+ memset (&pids, 0, sizeof (pids));
+
+ pids[0].pid = 0; // PAT
+ pids[1].pid = -1;
+
+ if (!quiet)
+ printf ("Tuning: source: %s, frequency: %i, PAT pid %i, "
+ "symbol rate %i\n", source>=0 ? "DVB-S(2)" :
+ source==-2 ? "DVB-T" : source==-3 ? "DVB-C" : "unknown",
+ si->cdata->frequency, pids[0].pid,
+ fep.u.qpsk.symbol_rate);
+
+ recv_tune (r, tuner_type, source, &sec, &fep, pids);
+
+#ifdef WIN32THREADS
+ CreateThread(NULL, 0, stream_watch, si, 0, NULL);
+#else
+ pthread_create (&si->t, NULL, stream_watch, si);
+#endif
+// printf("mcli_setup %p\n",si);
+ return si;
+}
+
+/*-------------------------------------------------------------------------*/
+size_t mcli_stream_access (void *handle, char **buf)
+{
+ stream_info_t *si = (stream_info_t *) handle;
+
+ if (!handle)
+ return 0;
+ if (si->stop)
+ return 0;
+
+ pthread_mutex_lock(&si->lock_bf);
+ if(!si->head) {
+ pthread_mutex_unlock(&si->lock_bf);
+ return 0;
+ }
+ si->rd=si->head;
+ si->head=si->head->next;
+ pthread_mutex_unlock(&si->lock_bf);
+
+ *buf=si->rd->data;
+ return TS_PER_UDP*188;
+}
+
+/*-------------------------------------------------------------------------*/
+size_t mcli_stream_part_access (void *handle, char **buf)
+{
+ stream_info_t *si = (stream_info_t *) handle;
+
+ if (!handle)
+ return 0;
+ if (si->stop)
+ return 0;
+
+ pthread_mutex_lock(&si->lock_ts);
+ if(!si->head) {
+ int len;
+
+ if(!si->wr)
+ len=0;
+ else {
+ si->rd=si->wr;
+ si->wr=NULL;
+ len=si->fill;
+ *buf=si->rd->data;
+ }
+ pthread_mutex_unlock(&si->lock_ts);
+ return len;
+ }
+ si->rd=si->head;
+ si->head=si->head->next;
+ pthread_mutex_unlock(&si->lock_ts);
+
+ *buf=si->rd->data;
+ return TS_PER_UDP*188;
+}
+
+/*-------------------------------------------------------------------------*/
+void mcli_stream_skip (void *handle)
+{
+ stream_info_t *si = (stream_info_t *) handle;
+
+ if (!handle)
+ return;
+
+ pthread_mutex_lock(&si->lock_bf);
+ si->rd->next=si->free;
+ si->free=si->rd;
+ pthread_mutex_unlock(&si->lock_bf);
+
+ si->rd=NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+int mcli_stream_stop (void *handle)
+{
+ if (handle) {
+ stream_info_t *si = (stream_info_t *) handle;
+ recv_info_t *r = si->r;
+ if (pthread_exist(si->t)) {
+ si->stop = 1;
+ pthread_join (si->t, NULL);
+ }
+
+ if (r) {
+ pthread_mutex_lock(&si->lock_ts);
+ register_ten_handler (r, NULL, NULL);
+ register_ts_handler (r, NULL, NULL);
+ pthread_mutex_unlock(&si->lock_ts);
+ recv_stop(r);
+ sleep(2);
+ recv_del (r);
+ }
+ if (si->psi.buf) {
+ free (si->psi.buf);
+ }
+
+ if (si->rd)
+ free (si->rd);
+ if (si->wr)
+ free (si->wr);
+ while (si->head) {
+ stream_buffer_t *e;
+ e = si->head;
+ si->head = si->head->next;
+ free (e);
+ }
+ while (si->free) {
+ stream_buffer_t *e;
+ e = si->free;
+ si->free = si->free->next;
+ free (e);
+ }
+
+ pthread_mutex_destroy(&si->lock_ts);
+ pthread_mutex_destroy(&si->lock_bf);
+ free (si);
+ }
+ return 0;
+}
+
+cmdline_t cmd = { 0 };
+
+/*-------------------------------------------------------------------------*/
+void mcli_startup (void)
+{
+#ifdef PTW32_STATIC_LIB
+ pthread_win32_process_attach_np();
+#endif
+ netceiver_info_list_t *nc_list = nc_get_list ();
+
+#if defined WIN32 || defined APPLE
+ cmd.mld_start = 1;
+#endif
+
+// printf ("Using Interface %s\n", cmd.iface);
+ recv_init (cmd.iface, cmd.port);
+
+ if (cmd.mld_start) {
+ mld_client_init (cmd.iface);
+ }
+#if 1
+ int n, i;
+ printf ("Looking for netceivers out there....\n");
+ while (1) {
+ nc_lock_list ();
+ for (n = 0; n < nc_list->nci_num; n++) {
+ netceiver_info_t *nci = nc_list->nci + n;
+ printf ("\nFound NetCeiver: %s\n", nci->uuid);
+ if (!quiet)
+ for (i = 0; i < nci->tuner_num; i++) {
+ printf (" Tuner: %s, Type %d\n",
+ nci->tuner[i].fe_info.name,
+ nci->tuner[i].fe_info.type);
+ }
+ }
+ nc_unlock_list ();
+ if (nc_list->nci_num) {
+ break;
+ }
+ sleep (1);
+ }
+#endif
+}
diff --git a/mcast/netcv2dvbip/mingw/Makefile b/mcast/netcv2dvbip/mingw/Makefile
new file mode 100644
index 0000000..aa6ac0d
--- /dev/null
+++ b/mcast/netcv2dvbip/mingw/Makefile
@@ -0,0 +1,19 @@
+OBJS=main.o parse.o mclilink.o siparser.o crc32.o clist.o stream.o thread.o misc.o streamer.o igmp.o iface.o
+EXE=netcv2dvbip.exe
+CC:=gcc
+CXX=g++
+CFLAGS:= -O2 -DCLIENT -DSTATICLIB -Wall -I../../common/win32/include -I../../common -I../../client
+LDFLAGS:= -s -L../../common/win32/lib
+LDLIBS:= -lmingwex -lmcli -liphlpapi -lpthreadGC2 -lws2_32 -lxml2 -lz
+
+all: $(EXE)
+
+$(EXE): $(OBJS)
+ $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
+
+%.o: ../%.c
+ $(CXX) -c $(CFLAGS) -o $@ $<
+
+clean:
+ @-del $(EXE) *.o *.la *~
+
diff --git a/mcast/netcv2dvbip/mingw/build.cmd b/mcast/netcv2dvbip/mingw/build.cmd
new file mode 100644
index 0000000..3d3efdb
--- /dev/null
+++ b/mcast/netcv2dvbip/mingw/build.cmd
@@ -0,0 +1,2 @@
+@set PATH=c:\MinGw\bin;%PATH%
+@mingw32-make
diff --git a/mcast/netcv2dvbip/mingw/clean.cmd b/mcast/netcv2dvbip/mingw/clean.cmd
new file mode 100644
index 0000000..e43e4db
--- /dev/null
+++ b/mcast/netcv2dvbip/mingw/clean.cmd
@@ -0,0 +1,2 @@
+@set PATH=c:\MinGw\bin;%PATH%
+@mingw32-make clean
diff --git a/mcast/netcv2dvbip/misc.c b/mcast/netcv2dvbip/misc.c
new file mode 100644
index 0000000..aff2958
--- /dev/null
+++ b/mcast/netcv2dvbip/misc.c
@@ -0,0 +1,193 @@
+#include <stdio.h>
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <pthread.h>
+#include "misc.h"
+
+void log_socket_error(const char* msg)
+{
+#ifdef WIN32
+ printf(msg);
+ printf(": WSAGetLastError(): %d", WSAGetLastError());
+#else
+ perror(msg);
+#endif
+}
+
+#ifdef WIN32
+static const unsigned __int64 epoch = 116444736000000000;
+
+int gettimeofday(struct timeval * tp, struct timezone * tzp)
+{
+ FILETIME file_time;
+ SYSTEMTIME system_time;
+ ULARGE_INTEGER ularge;
+
+ GetSystemTime(&system_time);
+ SystemTimeToFileTime(&system_time, &file_time);
+ ularge.LowPart = file_time.dwLowDateTime;
+ ularge.HighPart = file_time.dwHighDateTime;
+
+ tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
+ tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
+
+ return 0;
+}
+#endif
+
+bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+{
+ struct timeval now;
+
+ if (!gettimeofday(&now, NULL)) {
+ now.tv_sec += MillisecondsFromNow / 1000;
+ now.tv_usec += (MillisecondsFromNow % 1000) * 1000;
+ if (now.tv_usec >= 1000000) {
+ now.tv_sec++;
+ now.tv_usec -= 1000000;
+ }
+ Abstime->tv_sec = now.tv_sec;
+ Abstime->tv_nsec = now.tv_usec * 1000;
+ return true;
+ }
+ return false;
+}
+
+// --- cTimeMs ---------------------------------------------------------------
+
+cTimeMs::cTimeMs(int Ms)
+{
+ Set(Ms);
+}
+
+uint64_t cTimeMs::Now(void)
+{
+ struct timeval t;
+
+ if (gettimeofday(&t, NULL) == 0)
+ return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
+ return 0;
+}
+
+void cTimeMs::Set(int Ms)
+{
+ begin = Now() + Ms;
+}
+
+bool cTimeMs::TimedOut(void)
+{
+ return Now() >= begin;
+}
+
+uint64_t cTimeMs::Elapsed(void)
+{
+ return Now() - begin;
+}
+
+#ifdef WIN32
+
+#ifdef __MINGW32__
+#define __try if(1)
+#define __leave goto out
+#define __finally if(1)
+#endif
+
+bool IsUserAdmin( bool* pbAdmin )
+{
+#if WINVER < 0x0500
+ HANDLE hAccessToken = NULL;
+ PBYTE pInfoBuffer = NULL;
+ DWORD dwInfoBufferSize = 1024; // starting buffer size
+ PTOKEN_GROUPS ptgGroups = NULL;
+#endif
+ PSID psidAdministrators = NULL;
+ SID_IDENTIFIER_AUTHORITY siaNtAuthority = {SECURITY_NT_AUTHORITY};
+ BOOL bResult = FALSE;
+
+__try
+{
+
+ // initialization of the security id
+ if( !AllocateAndInitializeSid(
+ &siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &psidAdministrators ) )
+ __leave;
+
+#if WINVER < 0x0500
+ // code for the OS < W2k
+ // open process token
+ if( !OpenProcessToken( GetCurrentProcess(),TOKEN_QUERY,&hAccessToken ) )
+ __leave;
+
+ do // lets make a buffer for the information from the token
+ {
+ if( pInfoBuffer )
+ delete pInfoBuffer;
+ pInfoBuffer = new BYTE[dwInfoBufferSize];
+ if( !pInfoBuffer )
+ __leave;
+ SetLastError( 0 );
+ if( !GetTokenInformation( hAccessToken, TokenGroups,
+ pInfoBuffer, dwInfoBufferSize, &dwInfoBufferSize ) &&
+ ( ERROR_INSUFFICIENT_BUFFER != GetLastError() ) )
+ __leave;
+ else
+ ptgGroups = (PTOKEN_GROUPS)pInfoBuffer;
+ }
+ while( GetLastError() ); // if we encounter an error, then the
+ // initializing buffer too small,
+ // lets make it bigger
+
+ // lets check the security id one by one, while we find one we need
+ for( UINT i = 0; i < ptgGroups->GroupCount; i++ )
+ {
+ if( EqualSid(psidAdministrators,ptgGroups->Groups[i].Sid) )
+ {
+ *pbAdmin = TRUE;
+ bResult = TRUE;
+ __leave;
+ }
+ }
+#else
+ // this is code for the W2K
+ bResult = CheckTokenMembership( NULL, psidAdministrators, pbAdmin );
+#endif
+}
+
+__finally
+{
+#ifdef __MINGW32__
+out:
+#endif
+#if WINVER < 0x0500
+ if( hAccessToken )
+ CloseHandle( hAccessToken );
+ if( pInfoBuffer )
+ delete pInfoBuffer;
+#endif
+ if( psidAdministrators )
+ FreeSid( psidAdministrators );
+}
+
+return bResult;
+}
+
+bool IsVistaOrHigher()
+{
+ OSVERSIONINFO osvi;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ GetVersionEx(&osvi);
+
+ return (osvi.dwMajorVersion >= 6);
+}
+
+#endif
diff --git a/mcast/netcv2dvbip/misc.h b/mcast/netcv2dvbip/misc.h
new file mode 100644
index 0000000..edc269d
--- /dev/null
+++ b/mcast/netcv2dvbip/misc.h
@@ -0,0 +1,67 @@
+#ifndef __MISC_H
+#define __MISC_H
+
+#ifdef _MSC_VER
+ typedef unsigned __int64 uint64_t; // Define it from MSVC's internal type
+#else
+ #include <stdint.h> // Use the C99 official header
+#endif
+
+#define bool int
+
+#ifndef __STL_CONFIG_H // in case some plugin needs to use the STL
+template<class T> inline T xmin(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T xmax(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int xsgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
+template<class T> inline void xswap(T &a, T &b) { T t = a; a = b; b = t; }
+#endif
+
+#ifdef WIN32
+typedef unsigned long in_addr_t;
+#define SCKT SOCKET
+#else
+#include <arpa/inet.h>
+#define SCKT int
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifdef WIN32
+#define strdup _strdup
+#endif
+
+#define HI_BYTE(byte) (((byte) >> 4) & 0x0F)
+#define LO_BYTE(byte) ((byte) & 0x0F)
+
+extern void log_socket_error(const char* msg);
+extern bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow);
+
+#ifdef WIN32
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of DST correction */
+};
+extern int gettimeofday(struct timeval * tp, struct timezone * tzp);
+bool IsUserAdmin( bool* pbAdmin );
+bool IsVistaOrHigher();
+#else
+#include <sys/time.h>
+#endif
+
+class cTimeMs {
+private:
+ uint64_t begin;
+public:
+ cTimeMs(int Ms = 0);
+ ///< Creates a timer with ms resolution and an initial timeout of Ms.
+ static uint64_t Now(void);
+ void Set(int Ms = 0);
+ bool TimedOut(void);
+ uint64_t Elapsed(void);
+};
+
+extern int quiet;
+
+#endif
diff --git a/mcast/netcv2dvbip/parse.c b/mcast/netcv2dvbip/parse.c
new file mode 100644
index 0000000..ab60e9e
--- /dev/null
+++ b/mcast/netcv2dvbip/parse.c
@@ -0,0 +1,496 @@
+#include "dvbipstream.h"
+
+typedef struct
+{
+ int userValue;
+ int driverValue;
+} tChannelParameterMap;
+
+const tChannelParameterMap RolloffValues[] = {
+ {0, 0},
+ {35, 0},
+ {25, 0},
+ {20, 0},
+ {-1}
+};
+
+const tChannelParameterMap InversionValues[] = {
+ {0, INVERSION_OFF},
+ {1, INVERSION_ON},
+ {999, INVERSION_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap BandwidthValues[] = {
+ {6, BANDWIDTH_6_MHZ},
+ {7, BANDWIDTH_7_MHZ},
+ {8, BANDWIDTH_8_MHZ},
+ {999, BANDWIDTH_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap CoderateValues[] = {
+ {0, FEC_NONE},
+ {12, FEC_1_2},
+ {23, FEC_2_3},
+ {34, FEC_3_4},
+ {45, FEC_4_5},
+ {56, FEC_5_6},
+ {67, FEC_6_7},
+ {78, FEC_7_8},
+ {89, FEC_8_9},
+ {999, FEC_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap CoderateValuesS[] = {
+ {0, FEC_NONE},
+ {12, FEC_1_2},
+ {23, FEC_2_3},
+ {34, FEC_3_4},
+ {45, FEC_4_5},
+ {56, FEC_5_6},
+ {67, FEC_6_7},
+ {78, FEC_7_8},
+ {89, FEC_8_9},
+ {13, FEC_1_3}, //S2
+ {14, FEC_1_4}, //S2
+ {25, FEC_2_5}, //S2
+ {35, FEC_3_5}, //S2
+ {910, FEC_9_10}, //S2
+ {999, FEC_AUTO},
+ {-1}
+};
+
+
+const tChannelParameterMap ModulationValues[] = {
+ {0, QPSK},
+ {16, QAM_16},
+ {32, QAM_32},
+ {64, QAM_64},
+ {128, QAM_128},
+ {256, QAM_256},
+ {999, QAM_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap ModulationValuesS[] = {
+ {2, QPSK},
+ {4, QPSK},
+ {5, PSK8},
+ {42, QPSK_S2}, // S2
+ {8, PSK8},
+ {16, QAM_16},
+ {32, QAM_32},
+ {64, QAM_64},
+ {128, QAM_128},
+ {256, QAM_256},
+ {999, QAM_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap TransmissionValues[] = {
+ {2, TRANSMISSION_MODE_2K},
+ {8, TRANSMISSION_MODE_8K},
+ {999, TRANSMISSION_MODE_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap GuardValues[] = {
+ {4, GUARD_INTERVAL_1_4},
+ {8, GUARD_INTERVAL_1_8},
+ {16, GUARD_INTERVAL_1_16},
+ {32, GUARD_INTERVAL_1_32},
+ {999, GUARD_INTERVAL_AUTO},
+ {-1}
+};
+
+const tChannelParameterMap HierarchyValues[] = {
+ {0, HIERARCHY_NONE},
+ {1, HIERARCHY_1},
+ {2, HIERARCHY_2},
+ {4, HIERARCHY_4},
+ {999, HIERARCHY_AUTO},
+ {-1}
+};
+
+#ifdef WIN32
+#define strtok_r mystrtok
+char *strtok_r(char *s, const char *d, char **m)
+{
+ char *p;
+ char *q;
+
+ if (s)
+ *m = s;
+
+ p = *m;
+ if (!p)
+ return NULL;
+
+ while (*p && strchr(d,*p))
+ p++;
+
+ if (*p == 0) {
+ *m = NULL;
+ return NULL;
+ }
+
+ q = p;
+ while (*q && !strchr(d,*q))
+ q++;
+
+ if (!*q)
+ *m = NULL;
+ else {
+ *q++ = 0;
+ *m = q;
+ }
+
+ return p;
+}
+#endif
+
+char *strreplace (char *s, char c1, char c2)
+{
+ if (s) {
+ char *p = s;
+ while (*p) {
+ if (*p == c1)
+ *p = c2;
+ p++;
+ }
+ }
+ return s;
+}
+
+
+int UserIndex (int Value, const tChannelParameterMap * Map)
+{
+ const tChannelParameterMap *map = Map;
+ while (map && map->userValue != -1) {
+ if (map->userValue == Value)
+ return map - Map;
+ map++;
+ }
+ return -1;
+}
+
+int DriverIndex (int Value, const tChannelParameterMap * Map)
+{
+ const tChannelParameterMap *map = Map;
+ while (map && map->userValue != -1) {
+ if (map->driverValue == Value)
+ return map - Map;
+ map++;
+ }
+ return -1;
+}
+
+int MapToDriver (int Value, const tChannelParameterMap * Map)
+{
+ int n = UserIndex (Value, Map);
+ if (n >= 0)
+ return Map[n].driverValue;
+ return -1;
+}
+
+static const char *ParseParameter (const char *s, int *Value,
+ const tChannelParameterMap * Map)
+{
+ if (*++s) {
+ char *p = NULL;
+ int n;
+ errno = 0;
+ n = strtol (s, &p, 10);
+ if (!errno && p != s) {
+ *Value = MapToDriver (n, Map);
+ if (*Value >= 0)
+ return p;
+ }
+ }
+ printf ("ERROR: invalid value for parameter '%c'\n", *(s - 1));
+ return NULL;
+}
+
+bool StringToParameters (const char *s, channel_t * ch)
+{
+ int dummy;
+ bool newformat = false;
+ int deliverysystem = -1;
+ const char * start = s;
+
+ while (s && *s) {
+ switch (toupper (*s)) {
+ case 'B':
+ s = ParseParameter (s, &ch->bandwidth, BandwidthValues);
+ break;
+ case 'C':
+ s = ParseParameter (s, &ch->coderateH, CoderateValuesS);
+ break;
+ case 'D':
+ s = ParseParameter (s, &ch->coderateL, CoderateValues);
+ break;
+ case 'O':
+ newformat = true;
+ case 'E':
+ s = ParseParameter (s, &dummy, RolloffValues);
+ break;
+ case 'G':
+ s = ParseParameter (s, &ch->guard, GuardValues);
+ break;
+ case 'H':
+ ch->polarization = SEC_VOLTAGE_18;
+ s++;
+ break;
+ case 'I':
+ s = ParseParameter (s, &ch->inversion, InversionValues);
+ break;
+ case 'L':
+ ch->polarization = SEC_VOLTAGE_18;
+ s++;
+ break;
+ case 'M':
+ s = ParseParameter (s, &ch->modulation,
+ ModulationValuesS);
+ break;
+ case 'R':
+ ch->polarization = SEC_VOLTAGE_13;
+ s++;
+ break;
+ case 'S':
+ newformat = true;
+ s++;
+ if (*s == '1')
+ deliverysystem = 1;
+ else if (*s == '0')
+ deliverysystem = 0;
+ s++;
+ break;
+ case 'T':
+ s = ParseParameter (s, &ch->transmission,
+ TransmissionValues);
+ break;
+ case 'V':
+ ch->polarization = SEC_VOLTAGE_13;
+ s++;
+ break;
+ case 'Y':
+ s = ParseParameter (s, &ch->hierarchy, HierarchyValues);
+ break;
+ default:
+ printf ("ERROR: unknown parameter key '%c' at pos %d\n",
+ *s, (int)((long)(s-start)));
+ return 0;
+ }
+ }
+ if (newformat)
+ {
+ //printf("Detected VDR-1.7.x parameter string format.\n");
+ if (deliverysystem == 1 && ch->modulation == QPSK)
+ {
+ ch->modulation = QPSK_S2;
+ }
+ }
+ return 1;
+}
+
+int SourceFromString (char *s)
+{
+ char t = 0, d = 0;
+ float val = 0;
+
+ sscanf (s, "%c%f%c", &t, &val, &d);
+ if (t == 'S') {
+ val = val * 10;
+ if (d == 'W')
+ val = 1800 - val;
+ else
+ val = val + 1800;
+ } else if (t == 'T')
+ val = -2;
+ else if (t == 'C')
+ val = -3;
+ return (int) val;
+}
+
+int ParseLine (const char *s, channel_t * ch)
+{
+ bool ok = 1;
+ memset (ch, 0, sizeof (channel_t));
+
+ if (*s == ':') {
+#if 0
+ groupSep = true;
+ if (*++s == '@' && *++s) {
+ char *p = NULL;
+ errno = 0;
+ int n = strtol (s, &p, 10);
+ if (!errno && p != s && n > 0) {
+ ch->number = n;
+ s = p;
+ }
+ }
+ ch->name = strdup (skipspace (s));
+ strreplace (ch->name, '|', ':');
+#endif
+ ok = 0;
+ } else {
+// groupSep = false;
+ char *namebuf = NULL;
+ char *sourcebuf = NULL;
+ char *parambuf = NULL;
+ char *vpidbuf = NULL;
+ char *apidbuf = NULL;
+ char *caidbuf = NULL;
+ int fields;
+#if ! (defined WIN32 || defined APPLE)
+ fields = sscanf (s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:"
+ "%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf,
+ &ch->frequency, &parambuf, &sourcebuf, &ch->srate,
+ &vpidbuf, &apidbuf, &ch->tpid, &caidbuf, &ch->sid,
+ &ch->nid, &ch->tid, &ch->rid);
+#else
+ namebuf = (char *) malloc (1024);
+ parambuf = (char *) malloc (1024);
+ sourcebuf = (char *) malloc (1024);
+ vpidbuf = (char *) malloc (1024);
+ apidbuf = (char *) malloc (1024);
+ caidbuf = (char *) malloc (1024);
+ fields = sscanf (s, "%[^:]:%d :%[^:]:%[^:] :%d :%[^:]:%[^:]:"
+ "%d :%[^:]:%d :%d :%d :%d ", namebuf, &ch->frequency,
+ parambuf, sourcebuf, &ch->srate, vpidbuf, apidbuf,
+ &ch->tpid, caidbuf, &ch->sid, &ch->nid, &ch->tid,
+ &ch->rid);
+
+#endif
+ if (fields >= 9) {
+ if (fields == 9) {
+ // allow reading of old format
+ ch->sid = atoi (caidbuf);
+ free (caidbuf);
+ caidbuf = NULL;
+ ch->caids[0] = ch->tpid;
+ ch->caids[1] = 0;
+ ch->tpid = 0;
+ }
+ ch->vpid = ch->ppid = 0;
+ ch->apids[0] = 0;
+ ch->dpids[0] = 0;
+ ok = false;
+ if (parambuf && sourcebuf && vpidbuf && apidbuf) {
+ char *p, *dpidbuf, *q, *strtok_next;
+// int NumApids;
+ ok = StringToParameters (parambuf, ch);
+ ch->source = SourceFromString (sourcebuf);
+ ok = ok && ((ch->source >= 0) ||
+ (ch->source == -2)||(ch->source == -3));
+ p = strchr (vpidbuf, '+');
+ if (p)
+ *p++ = 0;
+ if (sscanf (vpidbuf, "%d", &ch->vpid) != 1)
+ return false;
+ if (p) {
+ if (sscanf (p, "%d", &ch->ppid) != 1)
+ return false;
+ } else
+ ch->ppid = ch->vpid;
+
+ dpidbuf = strchr (apidbuf, ';');
+ if (dpidbuf)
+ *dpidbuf++ = 0;
+ p = apidbuf;
+
+ ch->NumApids = 0;
+
+ while ((q = strtok_r (p, ",", &strtok_next))) {
+ if (ch->NumApids < MAXAPIDS) {
+ ch->apids[ch->NumApids++] =
+ strtol (q, NULL, 10);
+ } else
+ // no need to set ok to 'false'
+ printf ("ERROR: too many APIDs!"
+ "\n");
+ p = NULL;
+ }
+ ch->apids[ch->NumApids] = 0;
+ if (dpidbuf) {
+ char *p = dpidbuf;
+ char *q;
+ int NumDpids = 0;
+ char *strtok_next;
+ while ((q=strtok_r(p, ",", &strtok_next))) {
+ if (NumDpids < MAXDPIDS) {
+ ch->dpids[ch->NumDpids++] =
+ strtol (q, NULL, 10);
+ } else
+ // no need to set ok to 'false'
+ printf ("ERROR: too many DPIDs!"
+ "\n");
+ p = NULL;
+ }
+ ch->dpids[ch->NumDpids] = 0;
+ }
+#if 0
+ if (caidbuf) {
+ char *p = caidbuf;
+ char *q;
+ int NumCaIds = 0;
+ char *strtok_next;
+ while ((q=strtok_r(p, ",", &strtok_next))) {
+ if (NumCaIds < MAXCAIDS) {
+ caids[NumCaIds++] =
+ strtol (q, NULL, 16) & 0xFFFF;
+ if (NumCaIds == 1 &&
+ caids[0] <= 0x00FF)
+ break;
+ } else
+ // no need to set ok to 'false'
+ printf ("ERROR: too many CA ids!"
+ \n");
+ p = NULL;
+ }
+ caids[NumCaIds] = 0;
+ }
+#endif
+ }
+ strreplace (namebuf, '|', ':');
+ {
+ char *p = strchr (namebuf, ';');
+ if (p) {
+ *p++ = 0;
+ ch->provider = strdup (p);
+ if (!ch->provider) {
+ printf ("ERROR: out of memory!\n");
+ ok = 0;
+ }
+ }
+ p = strchr (namebuf, ',');
+ if (p) {
+ *p++ = 0;
+ ch->shortName = strdup (p);
+ if (!ch->shortName) {
+ printf ("ERROR: out of memory!\n");
+ ok = 0;
+ }
+ }
+ }
+ ch->name = strdup (namebuf);
+ if (!ch->name) {
+ printf ("ERROR: out of memory!\n");
+ ok = 0;
+ }
+
+ } else
+ ok = 0;
+#if (defined WIN32 || defined APPLE)
+ free (parambuf);
+ free (sourcebuf);
+ free (vpidbuf);
+ free (apidbuf);
+ free (caidbuf);
+ free (namebuf);
+#endif
+ }
+ return ok;
+}
diff --git a/mcast/netcv2dvbip/siparser.c b/mcast/netcv2dvbip/siparser.c
new file mode 120000
index 0000000..a2f9177
--- /dev/null
+++ b/mcast/netcv2dvbip/siparser.c
@@ -0,0 +1 @@
+../common/siparser.c \ No newline at end of file
diff --git a/mcast/netcv2dvbip/stream.c b/mcast/netcv2dvbip/stream.c
new file mode 100644
index 0000000..8d57f28
--- /dev/null
+++ b/mcast/netcv2dvbip/stream.c
@@ -0,0 +1,185 @@
+#include "dvbipstream.h"
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#endif
+
+#include <pthread.h>
+#include <sys/types.h>
+
+#include "clist.h"
+#include "stream.h"
+#include "thread.h"
+
+#define SLEEPTIME (20*1000)
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+cStream::cStream(int Channum, in_addr_t Addr, int portnum)
+ : cThread("udp streamer")
+{
+ handle = 0;
+ channum = Channum;
+ addr = Addr;
+ m_portnum = portnum;
+}
+
+cStream::~cStream(void)
+{
+}
+
+bool cStream::StartStream(in_addr_t bindaddr)
+{
+ int rc=0;
+
+ peer.sin_family = AF_INET;
+ peer.sin_port = htons(m_portnum);
+ peer.sin_addr.s_addr = addr;
+
+ udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (udp_socket < 0)
+ {
+ log_socket_error("Stream: socket()");
+ return false;
+ }
+
+#if 0
+ //----------------------
+ // Bind the socket.
+ sockaddr_in m_LocalAddr;
+
+ m_LocalAddr.sin_family = AF_INET;
+ m_LocalAddr.sin_port = htons(m_portnum-1);
+ m_LocalAddr.sin_addr.s_addr = bindaddr;
+
+ rc = ::bind( udp_socket, (struct sockaddr*)&m_LocalAddr,
+ sizeof(m_LocalAddr) );
+ if ( rc < 0 )
+ {
+ log_socket_error("STREAM bind()");
+ return false;
+ }
+#endif
+#if 0
+ char loop = 1;
+ rc = setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char*)&loop, sizeof(loop));
+ if ( rc < 0 )
+ {
+ log_socket_error("STREAM setsockopt(IP_MULTICAST_LOOP)");
+ return false;
+ }
+#endif
+ rc = setsockopt(udp_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&bindaddr, sizeof(bindaddr));
+ if ( rc < 0 )
+ {
+ log_socket_error("STREAM setsockopt(IP_MULTICAST_IF)");
+ return false;
+ }
+
+
+ pthread_mutex_lock(&lock);
+
+ if (handle == 0)
+ {
+ handle = mcli_stream_setup(channum);
+ if (handle)
+ Start();
+ }
+ else
+ {
+ printf("cStream: handle != NULL !\n");
+ }
+ pthread_mutex_unlock(&lock);
+
+ return true;
+
+}
+
+void cStream::Action()
+{
+ unsigned int retries;
+ size_t len;
+ char *ptr;
+ struct pollfd p;
+
+ p.fd = udp_socket;
+ p.events = POLLOUT;
+
+ while (Running())
+ {
+ for (retries=1;;retries++) {
+ if (retries&0xff)
+ len = mcli_stream_access (handle, &ptr);
+ else
+ len = mcli_stream_part_access (handle, &ptr);
+ if (len)
+ break;
+ if(!Running())goto out;
+ usleep (SLEEPTIME);
+ }
+
+ switch (poll(&p,1,100)) {
+ case -1:
+ log_socket_error( "STREAM poll()" );
+ case 0:
+ usleep (SLEEPTIME);
+ continue;
+ default:
+ if (!(p.revents&POLLOUT)) {
+ usleep (SLEEPTIME);
+ continue;
+ }
+ if (sendto( udp_socket, ptr, len, 0,
+ (struct sockaddr *)&peer, sizeof(peer) ) < 0) {
+#ifndef WIN32
+ if ((errno == EINTR)||(errno == EWOULDBLOCK)) {
+ usleep (SLEEPTIME);
+ continue;
+ }
+#else
+ int rc;
+ rc = WSAGetLastError();
+ if ((rc == WSAEINTR)||(rc == WSAEWOULDBLOCK)) {
+ usleep (SLEEPTIME);
+ continue;
+ }
+#endif
+ log_socket_error("STREAM: sendto()");
+ goto out;
+ }
+ break;
+ }
+
+ mcli_stream_skip (handle);
+ }
+out:;
+}
+
+void cStream::StopStream()
+{
+ Cancel(3);
+
+ pthread_mutex_lock(&lock);
+
+ if (handle)
+ {
+ mcli_stream_stop(handle);
+ handle = 0;
+ }
+ else
+ {
+ printf("cStream: handle == NULL !\n");
+ }
+ pthread_mutex_unlock(&lock);
+
+#ifdef WIN32
+ closesocket(udp_socket);
+#else
+ close(udp_socket);
+#endif
+
+}
+
diff --git a/mcast/netcv2dvbip/stream.h b/mcast/netcv2dvbip/stream.h
new file mode 100644
index 0000000..1e9464a
--- /dev/null
+++ b/mcast/netcv2dvbip/stream.h
@@ -0,0 +1,31 @@
+#ifndef __STREAM_H
+#define __STREAM_H
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+#include "clist.h"
+#include "thread.h"
+#include "misc.h"
+
+class cStream : public cListObject, public cThread
+{
+ public:
+ cStream(int channum, in_addr_t addr, int portnum);
+ ~cStream(void);
+ bool StartStream(in_addr_t bindaddr);
+ void StopStream();
+
+ private:
+ void *handle;
+ SCKT udp_socket;
+ struct sockaddr_in peer;
+ int channum;
+ in_addr_t addr;
+ int m_portnum;
+
+ virtual void Action();
+};
+
+#endif
diff --git a/mcast/netcv2dvbip/streamer.c b/mcast/netcv2dvbip/streamer.c
new file mode 100644
index 0000000..6403386
--- /dev/null
+++ b/mcast/netcv2dvbip/streamer.c
@@ -0,0 +1,101 @@
+#include "defs.h"
+
+#include <stdio.h>
+
+#include "misc.h"
+#include "igmp.h"
+#include "streamer.h"
+#include "stream.h"
+
+#define MULTICAST_PRIV_MIN (0xEFFF0000)
+#define MULTICAST_PRIV_MAX (0xEFFF1000)
+
+cStreamer::cStreamer()
+{
+ m_IgmpMain = NULL;
+ m_bindaddr = 0;
+ m_portnum = 0;
+ m_table = 0;
+}
+
+void cStreamer::SetBindIf(iface_t bindif)
+{
+ m_bindaddr = bindif.ipaddr;
+ m_bindif = bindif;
+}
+
+void cStreamer::SetStreamPort(int portnum)
+{
+ m_portnum = portnum;
+}
+
+void cStreamer::SetTable(int table)
+{
+ m_table = table;
+}
+
+void cStreamer::Run()
+{
+ if ( m_IgmpMain == NULL )
+ {
+ m_IgmpMain = new cIgmpMain(this, m_bindif, m_table);
+ m_IgmpMain->StartListener();
+ }
+ return;
+}
+
+void cStreamer::Stop()
+{
+ if ( m_IgmpMain )
+ {
+ m_IgmpMain->Destruct();
+ delete m_IgmpMain;
+ m_IgmpMain = NULL;
+ }
+ return;
+}
+
+void cStreamer::SetNumGroups(int numgroups)
+{
+ m_numgroups = numgroups;
+}
+
+bool cStreamer::IsGroupinRange(in_addr_t groupaddr)
+{
+ in_addr_t g = htonl(groupaddr);
+ if ( (g > MULTICAST_PRIV_MIN) && (g <= MULTICAST_PRIV_MIN+m_numgroups)
+ &&(g <= MULTICAST_PRIV_MAX) )
+ {
+ return true;
+ }
+ return false;
+}
+
+void cStreamer::StartMulticast(cMulticastGroup* Group)
+{
+ in_addr group;
+ group.s_addr = Group->group;
+ unsigned long channel = htonl(Group->group) - MULTICAST_PRIV_MIN;
+
+ printf("START Channel %d on Multicast Group: %s\n",
+ (unsigned short) channel, inet_ntoa(group));
+ if (Group->stream == NULL)
+ {
+ Group->stream = new cStream(channel, Group->group, m_portnum);
+ Group->stream->StartStream(m_bindaddr);
+ }
+}
+
+void cStreamer::StopMulticast(cMulticastGroup* Group)
+{
+ in_addr group;
+ group.s_addr = Group->group;
+
+ printf("STOP Multicast Group: %s\n", inet_ntoa(group));
+ if (Group->stream)
+ {
+ Group->stream->StopStream();
+ delete Group->stream;
+ Group->stream = NULL;
+ }
+}
diff --git a/mcast/netcv2dvbip/streamer.h b/mcast/netcv2dvbip/streamer.h
new file mode 100644
index 0000000..cc9388d
--- /dev/null
+++ b/mcast/netcv2dvbip/streamer.h
@@ -0,0 +1,35 @@
+#ifndef __STREAMER_H
+#define __STREAMER_H
+
+#include "misc.h"
+
+class cMulticastGroup;
+class cIgmpMain;
+
+class cStreamer
+{
+ public:
+ cStreamer();
+
+ void Run();
+ void Stop();
+ void SetBindIf(iface_t bindif);
+ void SetStreamPort(int portnum);
+ void SetTable(int table);
+ void SetNumGroups(int numgroups);
+
+ bool IsGroupinRange(in_addr_t groupaddr);
+ void StartMulticast(cMulticastGroup* Group);
+ void StopMulticast(cMulticastGroup* Group);
+
+
+ private:
+ cIgmpMain* m_IgmpMain;
+ in_addr_t m_bindaddr;
+ iface_t m_bindif;
+ int m_table;
+ int m_portnum;
+ int m_numgroups;
+};
+
+#endif
diff --git a/mcast/netcv2dvbip/thread.c b/mcast/netcv2dvbip/thread.c
new file mode 100644
index 0000000..1879ebb
--- /dev/null
+++ b/mcast/netcv2dvbip/thread.c
@@ -0,0 +1,327 @@
+#include "defs.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#ifndef APPLE
+#include <malloc.h>
+#endif
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+
+#include "thread.h"
+#include "misc.h"
+
+// --- cCondWait -------------------------------------------------------------
+
+cCondWait::cCondWait(void)
+{
+ signaled = false;
+ pthread_mutex_init(&mutex, NULL);
+ pthread_cond_init(&cond, NULL);
+}
+
+cCondWait::~cCondWait()
+{
+ pthread_cond_broadcast(&cond); // wake up any sleepers
+ pthread_cond_destroy(&cond);
+ pthread_mutex_destroy(&mutex);
+}
+
+void cCondWait::SleepMs(int TimeoutMs)
+{
+ cCondWait w;
+ // making sure the time is >2ms to avoid a possible busy wait
+ // (no longer true for later 2.6 kernels)
+ w.Wait(xmax(TimeoutMs, 3));
+}
+
+bool cCondWait::Wait(int TimeoutMs)
+{
+ pthread_mutex_lock(&mutex);
+ if (!signaled) {
+ if (TimeoutMs) {
+ struct timespec abstime;
+ if (GetAbsTime(&abstime, TimeoutMs)) {
+ while (!signaled) {
+ if (pthread_cond_timedwait(&cond,
+ &mutex, &abstime) == ETIMEDOUT)
+ break;
+ }
+ }
+ }
+ else
+ pthread_cond_wait(&cond, &mutex);
+ }
+ bool r = signaled;
+ signaled = false;
+ pthread_mutex_unlock(&mutex);
+ return r;
+}
+
+void cCondWait::Signal(void)
+{
+ pthread_mutex_lock(&mutex);
+ signaled = true;
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+ pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+ pthread_cond_broadcast(&cond); // wake up any sleepers
+ pthread_cond_destroy(&cond);
+}
+
+void cCondVar::Wait(cMutex &Mutex)
+{
+ if (Mutex.locked) {
+ int locked = Mutex.locked;
+ Mutex.locked = 0; // have to clear the locked count here,
+ // as pthread_cond_wait
+ // does an implicit unlock of the mutex
+ pthread_cond_wait(&cond, &Mutex.mutex);
+ Mutex.locked = locked;
+ }
+}
+
+bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+{
+ bool r = true; // true = condition signaled, false = timeout
+
+ if (Mutex.locked) {
+ struct timespec abstime;
+ if (GetAbsTime(&abstime, TimeoutMs)) {
+ int locked = Mutex.locked;
+ Mutex.locked = 0; // have to clear the locked count
+ // here, as pthread_cond_timedwait
+ // does an implicit mutex unlock.
+ if (pthread_cond_timedwait(&cond, &Mutex.mutex,
+ &abstime) == ETIMEDOUT)
+ r = false;
+ Mutex.locked = locked;
+ }
+ }
+ return r;
+}
+
+void cCondVar::Broadcast(void)
+{
+ pthread_cond_broadcast(&cond);
+}
+
+
+
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+ locked = 0;
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+#ifndef APPLE
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+#endif
+ pthread_mutex_init(&mutex, &attr);
+}
+
+cMutex::~cMutex()
+{
+ pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+ pthread_mutex_lock(&mutex);
+ locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+ pthread_mutex_unlock(&mutex);
+}
+//---cMutex--------------------------------------------------------------------
+
+
+cThread::cThread(const char *Description)
+{
+ active = running = false;
+#ifdef WIN32
+ memset(&childTid, 0, sizeof(childTid));
+#else
+ childTid = 0;
+#endif
+ description = NULL;
+ if (Description)
+ {
+ description = strdup(Description);
+ }
+}
+
+cThread::~cThread()
+{
+ Cancel(); // just in case the derived class didn't call it
+ free(description);
+}
+
+void *cThread::StartThread(cThread *Thread)
+{
+ if (Thread->description && !quiet)
+ printf("%s thread started.\n", Thread->description);
+
+ Thread->Action();
+ if (Thread->description && !quiet)
+ printf("%s thread ended.\n", Thread->description);
+ Thread->running = false;
+ Thread->active = false;
+ return NULL;
+}
+
+// ms to wait for a thread to stop before newly starting it
+#define THREAD_STOP_TIMEOUT 3000
+// ms to sleep while waiting for a thread to stop
+#define THREAD_STOP_SLEEP 30
+
+bool cThread::Start(void)
+{
+ if (!running) {
+ if (active) {
+ // Wait until the previous incarnation
+ // of this thread has completely ended
+ // before starting it newly:
+ cTimeMs RestartTimeout;
+ while (!running && active && RestartTimeout.Elapsed() <
+ THREAD_STOP_TIMEOUT)
+ cCondWait::SleepMs(THREAD_STOP_SLEEP);
+ }
+ if (!active) {
+ active = running = true;
+ if (pthread_create(&childTid, NULL, (void *(*) (void *))
+ &StartThread, (void *)this) == 0) {
+ pthread_detach(childTid); // auto-reap
+ } else {
+ printf("%s thread cannot be created.\n",
+ description);
+ active = running = false;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+bool cThread::Active(void)
+{
+ if (active) {
+ //
+ // Single UNIX Spec v2 says:
+ //
+ // The pthread_kill() function is used to request
+ // that a signal be delivered to the specified thread.
+ //
+ // As in kill(), if sig is zero, error checking is
+ // performed but no signal is actually sent.
+ //
+ int err;
+ if ((err = pthread_kill(childTid, 0)) != 0) {
+ if (err != ESRCH)
+ printf("%s thread cannot be killed.\n", description);
+#ifdef WIN32
+ memset(&childTid, 0, sizeof(childTid));
+#else
+ childTid = 0;
+#endif
+ active = running = false;
+ } else
+ return true;
+ }
+ return false;
+}
+
+void cThread::Cancel(int WaitSeconds)
+{
+ running = false;
+ if (active && WaitSeconds > -1) {
+ if (WaitSeconds > 0) {
+ for (time_t t0 = time(NULL) + WaitSeconds;
+ time(NULL) < t0; ) {
+ if (!Active())
+ return;
+ cCondWait::SleepMs(10);
+ }
+ printf("ERROR: %s thread won't end (waited %d seconds)"
+ " - canceling it...\n", description ?
+ description : "", WaitSeconds);
+ }
+ pthread_cancel(childTid);
+#ifdef WIN32
+ memset(&childTid, 0, sizeof(childTid));
+#else
+ childTid = 0;
+#endif
+ active = false;
+ }
+}
+
+// --- cMutexLock ------------------------------------------------------------
+
+cMutexLock::cMutexLock(cMutex *Mutex)
+{
+ mutex = NULL;
+ locked = false;
+ Lock(Mutex);
+}
+
+cMutexLock::~cMutexLock()
+{
+ if (mutex && locked)
+ mutex->Unlock();
+}
+
+bool cMutexLock::Lock(cMutex *Mutex)
+{
+ if (Mutex && !mutex) {
+ mutex = Mutex;
+ Mutex->Lock();
+ locked = true;
+ return true;
+ }
+ return false;
+}
+
+// --- cThreadLock -----------------------------------------------------------
+
+cThreadLock::cThreadLock(cThread *Thread)
+{
+ thread = NULL;
+ locked = false;
+ Lock(Thread);
+}
+
+cThreadLock::~cThreadLock()
+{
+ if (thread && locked)
+ thread->Unlock();
+}
+
+bool cThreadLock::Lock(cThread *Thread)
+{
+ if (Thread && !thread) {
+ thread = Thread;
+ Thread->Lock();
+ locked = true;
+ return true;
+ }
+ return false;
+}
diff --git a/mcast/netcv2dvbip/thread.h b/mcast/netcv2dvbip/thread.h
new file mode 100644
index 0000000..f33172c
--- /dev/null
+++ b/mcast/netcv2dvbip/thread.h
@@ -0,0 +1,131 @@
+#ifndef __THREAD_H
+#define __THREAD_H
+
+#include <pthread.h>
+#include "misc.h"
+
+class cCondWait {
+private:
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ bool signaled;
+public:
+ cCondWait(void);
+ ~cCondWait();
+ static void SleepMs(int TimeoutMs);
+ ///< Creates a cCondWait object and uses it to sleep for TimeoutMs
+ ///< milliseconds, immediately giving up the calling thread's time
+ ///< slice and thus avoiding a "busy wait".
+ ///< In order to avoid a possible busy wait, TimeoutMs will be
+ ///< automatically limited to values >2.
+ bool Wait(int TimeoutMs = 0);
+ ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+ ///< forever if TimeoutMs is 0.
+ ///< \return Returns true if Signal() has been called, false it the given
+ ///< timeout has expired.
+ void Signal(void);
+ ///< Signals a caller of Wait() that the condition it is waiting for is
+ ///< met.
+};
+
+class cMutex;
+
+class cCondVar {
+private:
+ pthread_cond_t cond;
+public:
+ cCondVar(void);
+ ~cCondVar();
+ void Wait(cMutex &Mutex);
+ bool TimedWait(cMutex &Mutex, int TimeoutMs);
+ void Broadcast(void);
+};
+
+class cMutex {
+ friend class cCondVar;
+private:
+ pthread_mutex_t mutex;
+ int locked;
+public:
+ cMutex(void);
+ ~cMutex();
+ void Lock(void);
+ void Unlock(void);
+};
+
+class cThread {
+friend class cThreadLock;
+private:
+ cMutex mutex;
+ bool active;
+ bool running;
+ pthread_t childTid;
+ char *description;
+ static void *StartThread(cThread *Thread);
+protected:
+ void Lock(void) { mutex.Lock(); }
+ void Unlock(void) { mutex.Unlock(); }
+ virtual void Action(void) = 0;
+ ///< A derived cThread class must implement the code it wants to
+ ///< execute as a separate thread in this function. If this is
+ ///< a loop, it must check Running() repeatedly to see whether
+ ///< it's time to stop.
+ bool Running(void) { return running; }
+ ///< Returns false if a derived cThread object shall leave its Action()
+ ///< function.
+ void Cancel(int WaitSeconds = 0);
+ ///< Cancels the thread by first setting 'running' to false, so that
+ ///< the Action() loop can finish in an orderly fashion and then waiting
+ ///< up to WaitSeconds seconds for the thread to actually end. If the
+ ///< thread doesn't end by itself, it is killed.
+ ///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
+ ///< returns immediately, without killing the thread.
+public:
+ cThread(const char *Description = NULL);
+ ///< Creates a new thread.
+ ///< If Description is present, a log file entry will be made when
+ ///< the thread starts and stops. The Start() function must be called
+ ///< to actually start the thread.
+ virtual ~cThread();
+ bool Start(void);
+ ///< Actually starts the thread.
+ ///< If the thread is already running, nothing happens.
+ bool Active(void);
+ ///< Checks whether the thread is still alive.
+ };
+
+// cMutexLock can be used to easily set a lock on mutex and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which
+// uses cMutexLock may itself use a cMutexLock to make one longer lock instead
+// of many short ones.
+
+class cMutexLock {
+private:
+ cMutex *mutex;
+ bool locked;
+public:
+ cMutexLock(cMutex *Mutex = NULL);
+ ~cMutexLock();
+ bool Lock(cMutex *Mutex);
+ };
+
+// cThreadLock can be used to easily set a lock in a thread and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which
+// uses cThreadLock may itself use a cThreadLock to make one longer lock instead
+// of many short ones.
+
+class cThreadLock {
+private:
+ cThread *thread;
+ bool locked;
+public:
+ cThreadLock(cThread *Thread = NULL);
+ ~cThreadLock();
+ bool Lock(cThread *Thread);
+ };
+
+#define LOCK_THREAD cThreadLock ThreadLock(this)
+
+#endif