diff --git a/Makefile b/Makefile index b9dd41c..35b6f8d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.18 2007/10/14 18:45:34 rahrenbe Exp $ +# $Id: Makefile,v 1.19 2007/10/15 20:06:38 ajhseppa Exp $ # Debugging on/off #IPTV_DEBUG = 1 @@ -58,8 +58,8 @@ endif ### The object files (add further files here): OBJS = $(PLUGIN).o config.o setup.o device.o streamer.o protocoludp.o \ - protocolhttp.o protocolfile.o sectionfilter.o sidscanner.o \ - statistics.o common.o + protocolhttp.o protocolfile.o protocolext.o sectionfilter.o \ + sidscanner.o statistics.o common.o ### The main target: diff --git a/device.c b/device.c index 49c690f..ee6d6fd 100644 --- a/device.c +++ b/device.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: device.c,v 1.68 2007/10/14 18:45:34 rahrenbe Exp $ + * $Id: device.c,v 1.69 2007/10/15 20:06:38 ajhseppa Exp $ */ #include "config.h" @@ -30,6 +30,7 @@ cIptvDevice::cIptvDevice(unsigned int Index) pUdpProtocol = new cIptvProtocolUdp(); pHttpProtocol = new cIptvProtocolHttp(); pFileProtocol = new cIptvProtocolFile(); + pExtProtocol = new cIptvProtocolExt(); pIptvStreamer = new cIptvStreamer(tsBuffer, &mutex); // Initialize filter pointers memset(&secfilters, '\0', sizeof(secfilters)); @@ -48,6 +49,7 @@ cIptvDevice::~cIptvDevice() DELETENULL(pUdpProtocol); DELETENULL(pHttpProtocol); DELETENULL(pFileProtocol); + DELETENULL(pExtProtocol); DELETENULL(tsBuffer); // Detach and destroy sid filter if (pSidScanner) { @@ -172,6 +174,11 @@ cString cIptvDevice::GetChannelSettings(const char *Param, int *IpPort, cIptvPro *Protocol = pFileProtocol; return addr; } + else if (sscanf(Param, "IPTV|EXT|%a[^|]|%u", &loc, IpPort) == 2) { + cString addr(loc, true); + *Protocol = pExtProtocol; + return addr; + } return NULL; } diff --git a/protocolext.c b/protocolext.c new file mode 100644 index 0000000..8ea4e39 --- /dev/null +++ b/protocolext.c @@ -0,0 +1,217 @@ +/* + * protocolext.c: IPTV plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id: protocolext.c,v 1.1 2007/10/15 20:06:38 ajhseppa Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "config.h" +#include "protocolext.h" + +cIptvProtocolExt::cIptvProtocolExt() +: listenPort(4321), + streamPort(1234), + socketDesc(-1), + readBufferLen(TS_SIZE * IptvConfig.GetReadBufferTsCount()), + isActive(false) +{ + debug("cIptvProtocolExt::cIptvProtocolExt()\n"); + streamAddr = strdup(""); + listenAddr = strdup("127.0.0.1"); + // Allocate receive buffer + readBuffer = MALLOC(unsigned char, readBufferLen); + if (!readBuffer) + error("ERROR: MALLOC() failed in ProtocolExt()"); +} + +cIptvProtocolExt::~cIptvProtocolExt() +{ + debug("cIptvProtocolExt::~cIptvProtocolExt()\n"); + // Drop the multicast group and close the socket + Close(); + // Free allocated memory + free(streamAddr); + free(listenAddr); + free(readBuffer); +} + +bool cIptvProtocolExt::OpenSocket(const int Port) +{ + debug("cIptvProtocolExt::OpenSocket()\n"); + // If socket is there already and it is bound to a different port, it must + // be closed first + if (Port != listenPort) { + debug("cIptvProtocolExt::OpenSocket(): Socket tear-down\n"); + CloseSocket(); + } + // Bind to the socket if it is not active already + if (socketDesc < 0) { + int yes = 1; + // Create socket + socketDesc = socket(PF_INET, SOCK_DGRAM, 0); + if (socketDesc < 0) { + char tmp[64]; + error("ERROR: socket(): %s", strerror_r(errno, tmp, sizeof(tmp))); + return false; + } + // Make it use non-blocking I/O to avoid stuck read calls + if (fcntl(socketDesc, F_SETFL, O_NONBLOCK)) { + char tmp[64]; + error("ERROR: fcntl(): %s", strerror_r(errno, tmp, sizeof(tmp))); + CloseSocket(); + return false; + } + // Allow multiple sockets to use the same PORT number + if (setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(yes)) < 0) { + char tmp[64]; + error("ERROR: setsockopt(): %s", strerror_r(errno, tmp, sizeof(tmp))); + CloseSocket(); + return false; + } + // Bind socket + memset(&sockAddr, '\0', sizeof(sockAddr)); + sockAddr.sin_family = AF_INET; + sockAddr.sin_port = htons(Port); + sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); + int err = bind(socketDesc, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); + if (err < 0) { + char tmp[64]; + error("ERROR: bind(): %s", strerror_r(errno, tmp, sizeof(tmp))); + CloseSocket(); + return false; + } + // Update stream port + streamPort = Port; + } + return true; +} + +void cIptvProtocolExt::CloseSocket(void) +{ + debug("cIptvProtocolExt::CloseSocket()\n"); + // Check if socket exists + if (socketDesc >= 0) { + close(socketDesc); + socketDesc = -1; + } +} + + +int cIptvProtocolExt::Read(unsigned char* *BufferAddr) +{ + //debug("cIptvProtocolExt::Read()\n"); + socklen_t addrlen = sizeof(sockAddr); + // Set argument point to read buffer + *BufferAddr = readBuffer; + // Wait for data + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 500000; + // Use select + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(socketDesc, &rfds); + int retval = select(socketDesc + 1, &rfds, NULL, NULL, &tv); + // Check if error + if (retval < 0) { + char tmp[64]; + error("ERROR: select(): %s", strerror_r(errno, tmp, sizeof(tmp))); + return -1; + } + // Check if data available + else if (retval) { + // Read data from socket + int len = recvfrom(socketDesc, readBuffer, readBufferLen, MSG_DONTWAIT, + (struct sockaddr *)&sockAddr, &addrlen); + if ((len > 0) && (readBuffer[0] == 0x47)) { + // Set argument point to read buffer + *BufferAddr = &readBuffer[0]; + return len; + } + else if (len > 3) { + // http://www.networksorcery.com/enp/rfc/rfc2250.txt + // version + unsigned int v = (readBuffer[0] >> 6) & 0x03; + // extension bit + unsigned int x = (readBuffer[0] >> 4) & 0x01; + // cscr count + unsigned int cc = readBuffer[0] & 0x0F; + // payload type + unsigned int pt = readBuffer[1] & 0x7F; + // header lenght + unsigned int headerlen = (3 + cc) * sizeof(uint32_t); + // check if extension + if (x) { + // extension header length + unsigned int ehl = (((readBuffer[headerlen + 2] & 0xFF) << 8) | (readBuffer[headerlen + 3] & 0xFF)); + // update header length + headerlen += (ehl + 1) * sizeof(uint32_t); + } + // Check that rtp is version 2, payload type is MPEG2 TS + // and payload contains multiple of TS packet data + if ((v == 2) && (pt == 33) && (((len - headerlen) % TS_SIZE) == 0)) { + // Set argument point to payload in read buffer + *BufferAddr = &readBuffer[headerlen]; + return (len - headerlen); + } + } + } + return 0; +} + +bool cIptvProtocolExt::Open(void) +{ + debug("cIptvProtocolExt::Open(): streamAddr=%s\n", streamAddr); + + // Here process execution is forked to prepare for script execution + // pid_t PID = fork(); + + // Now run the script with the forked process + // if (pid != 0) + // execve(...) + + return true; +} + +bool cIptvProtocolExt::Close(void) +{ + debug("cIptvProtocolExt::Close(): streamAddr=%s\n", streamAddr); + // Close the socket + CloseSocket(); + + // Now the executed process should be terminated if it still exists + // exit(); + + // Wait for child termination + // wait(); + + return true; +} + +bool cIptvProtocolExt::Set(const char* Address, const int Port) +{ + debug("cIptvProtocolExt::Set(): %s:%d\n", Address, Port); + if (!isempty(Address)) { + // Update stream address and port + streamAddr = strcpyrealloc(streamAddr, Address); + streamPort = Port; + } + return true; +} + +cString cIptvProtocolExt::GetInformation(void) +{ + //debug("cIptvProtocolExt::GetInformation()"); + return cString::sprintf("ext://%s:%d", streamAddr, streamPort); +} diff --git a/protocolext.h b/protocolext.h new file mode 100644 index 0000000..7840849 --- /dev/null +++ b/protocolext.h @@ -0,0 +1,42 @@ +/* + * protocolext.h: IPTV plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id: protocolext.h,v 1.1 2007/10/15 20:06:38 ajhseppa Exp $ + */ + +#ifndef __IPTV_PROTOCOLEXT_H +#define __IPTV_PROTOCOLEXT_H + +#include +#include "protocolif.h" + +class cIptvProtocolExt : public cIptvProtocolIf { +private: + char* listenAddr; + int listenPort; + char* streamAddr; + int streamPort; + int socketDesc; + unsigned char* readBuffer; + unsigned int readBufferLen; + struct sockaddr_in sockAddr; + bool isActive; + +private: + bool OpenSocket(const int Port); + void CloseSocket(void); + +public: + cIptvProtocolExt(); + virtual ~cIptvProtocolExt(); + virtual int Read(unsigned char* *BufferAddr); + virtual bool Set(const char* Address, const int Port); + virtual bool Open(void); + virtual bool Close(void); + virtual cString GetInformation(void); +}; + +#endif // __IPTV_PROTOCOLEXT_H +