diff --git a/Makefile b/Makefile index cf77245..e153b38 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.6 2007/09/16 12:18:15 ajhseppa Exp $ +# $Id: Makefile,v 1.7 2007/09/19 15:14:32 ajhseppa Exp $ # Debugging on/off #IPTV_DEBUG = 1 @@ -57,7 +57,7 @@ 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 +OBJS = $(PLUGIN).o config.o setup.o device.o streamer.o protocoludp.o protocolhttp.o protocolfile.o pidfilter.o ### The main target: diff --git a/device.c b/device.c index 7910fb1..9daba78 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.23 2007/09/18 18:48:10 rahrenbe Exp $ + * $Id: device.c,v 1.24 2007/09/19 15:14:32 ajhseppa Exp $ */ #include "common.h" @@ -25,6 +25,8 @@ cIptvDevice::cIptvDevice(unsigned int Index) debug("cIptvDevice::cIptvDevice(%d)\n", deviceIndex); tsBuffer = new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()), (TS_SIZE * 2), false, "IPTV"); + memset(&filter, '\0', sizeof(filter)); + init_trans(&filter); tsBuffer->SetTimeouts(100, 100); // pad prefill to multiple of TS_SIZE tsBufferPrefill = MEGABYTE(IptvConfig.GetTsBufferSize()) * @@ -36,6 +38,12 @@ cIptvDevice::cIptvDevice(unsigned int Index) pFileProtocol = new cIptvProtocolFile(); pIptvStreamer = new cIptvStreamer(tsBuffer, &mutex); StartSectionHandler(); + + for(int i = 0; i < 32; ++i) { + filters[i].active = false; + filters[i].fifoDesc = -1; + } + } cIptvDevice::~cIptvDevice() @@ -44,6 +52,18 @@ cIptvDevice::~cIptvDevice() delete pIptvStreamer; delete pUdpProtocol; delete tsBuffer; + + // Iterate over all filters and clear their settings + for (int i = 0; i < 32; ++i) { + if (filters[i].active) { + close(filters[i].fifoDesc); + unlink(filters[i].pipeName); + memset(filters[i].pipeName, '\0', sizeof(filters[i].pipeName)); + filters[i].fifoDesc = -1; + filters[i].active = false; + clear_trans_filt(&filter, i); + } + } } bool cIptvDevice::Initialize(unsigned int DeviceCount) @@ -156,17 +176,46 @@ bool cIptvDevice::SetPid(cPidHandle *Handle, int Type, bool On) int cIptvDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) { - int pipeDesc[2]; - debug("cIptvDevice::OpenFilter(): Pid=%d Tid=%02X Mask=%02X\n", Pid, Tid, Mask); - // Create a pipe - if (pipe(pipeDesc) < 0) { - char tmp[64]; - error("ERROR: pipe(): %s", strerror_r(errno, tmp, sizeof(tmp))); - } - // Write pipe is used by the pid filtering class - // cIptvSectionFilter Filter(pipeDesc[1], Pid, Tid, Mask) - // Give the read pipe to vdr - return pipeDesc[0]; + + // Search the next free filter slot + for (int i = 0; i < 32; ++i) { + if(!filters[i].active) { + + debug("cIptvDevice::OpenFilter(): Pid=0x%X Tid=%02X Mask=%02X, filterCount = %d\n", Pid, Tid, Mask, i); + + uint8_t mask = Mask; + uint8_t filt = Tid; + + int err = set_trans_filt(&filter, i, Pid, &mask, &filt, 0); + if (err < 0) + error("Cannot set filter %d\n", i); + + memset(filters[i].pipeName, '\0', sizeof(filters[i].pipeName)); + snprintf(filters[i].pipeName, sizeof(filters[i].pipeName), + "/tmp/iptvPipe.%d", i); + err = mknod(filters[i].pipeName, 0644 | S_IFIFO, 0); + if (err < 0) { + char tmp[64]; + error("ERROR: mknod(): %s", strerror_r(errno, tmp, sizeof(tmp))); + unlink(filters[i].pipeName); + } + + // Create descriptors + int fifoDescriptor = open(filters[i].pipeName, O_RDWR | O_NONBLOCK); + int returnDescriptor = open(filters[i].pipeName, O_RDONLY | O_NONBLOCK); + + // Store the write pipe and set active flag + filters[i].active = true; + filters[i].fifoDesc = fifoDescriptor; + + //++filterCount; + return returnDescriptor; + + } + + } + // No free filter slot found + return -1; } bool cIptvDevice::OpenDvr(void) @@ -215,6 +264,55 @@ bool cIptvDevice::GetTSPacket(uchar *&Data) } isPacketDelivered = true; Data = p; + memcpy(filter.packet, p, sizeof(filter.packet)); + trans_filt(p, 188, &filter); + + + for(int i = 0; i < 32; ++i) { + + if(filters[i].active) { + + section *filtered = get_filt_sec(&filter, i); + + if (filtered->found) { + + // Select on the fifo emptyness. Using null timeout to return + // immediately + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(filters[i].fifoDesc, &rfds); + int retval = select(filters[i].fifoDesc + 1, &rfds, NULL, NULL, + &tv); + // Check if error + if (retval < 0) { + char tmp[64]; + error("ERROR: select(): %s", strerror_r(errno, tmp, sizeof(tmp))); + + // VDR has probably closed the filter file descriptor. + // Clear the filter + close(filters[i].fifoDesc); + unlink(filters[i].pipeName); + memset(filters[i].pipeName, '\0', sizeof(filters[i].pipeName)); + filters[i].fifoDesc = -1; + filters[i].active = false; + clear_trans_filt(&filter, i); + } + // There is no data in the fifo, more can be written + if (!retval) { + int err = write(filters[i].fifoDesc, filtered->payload, + filtered->length+3); + if (err < 0) { + char tmp[64]; + error("ERROR: write(): %s", strerror_r(errno, tmp, + sizeof(tmp))); + } + } + } + } + } return true; } Data = NULL; diff --git a/device.h b/device.h index 6952428..b4f69fc 100644 --- a/device.h +++ b/device.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: device.h,v 1.9 2007/09/16 13:11:19 rahrenbe Exp $ + * $Id: device.h,v 1.10 2007/09/19 15:14:32 ajhseppa Exp $ */ #ifndef __IPTV_DEVICE_H @@ -16,6 +16,16 @@ #include "protocolfile.h" #include "streamer.h" +#include "pidfilter.h" + +struct filterInfo { + bool active; + int fifoDesc; + char pipeName[128]; + int lastProvided; +}; + + class cIptvDevice : public cDevice { // static ones public: @@ -37,6 +47,8 @@ private: //cIptvProtocolRtsp *pRtspProtocol; cIptvStreamer *pIptvStreamer; cMutex mutex; + trans filter; + filterInfo filters[32]; // constructor & destructor public: diff --git a/pidfilter.c b/pidfilter.c new file mode 100644 index 0000000..a52e7e9 --- /dev/null +++ b/pidfilter.c @@ -0,0 +1,334 @@ +/* + * dvb-mpegtools for the Siemens Fujitsu DVB PCI card + * + * Copyright (C) 2000, 2001 Marcus Metzler + * for convergence integrated media GmbH + * Copyright (C) 2002 Marcus Metzler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + + * The author can be reached at mocm@metzlerbros.de, + */ + +#include "pidfilter.h" + +#define PID_MASK_HI 0x1F +uint16_t get_pid(uint8_t *pid) +{ + uint16_t pp = 0; + + pp = (pid[0] & PID_MASK_HI)<<8; + pp |= pid[1]; + + return pp; +} + + +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define PAY_START 0x40 +/* + conversion +*/ + +void init_trans(trans *p) +{ + int i; + + p->found = 0; + p->pes = 0; + p->is_full = 0; + p->pes_start = 0; + p->pes_started = 0; + p->set = 0; + + for (i = 0; i < MASKL*MAXFILT ; i++){ + p->mask[i] = 0; + p->filt[i] = 0; + } + for (i = 0; i < MAXFILT ; i++){ + p->sec[i].found = 0; + p->sec[i].length = 0; + } +} + +int set_trans_filt(trans *p, int filtn, uint16_t pid, uint8_t *mask, uint8_t *filt, int pes) +{ + int i; + int off; + + if ( filtn > MAXFILT-1 || filtn<0 ) return -1; + p->pid[filtn] = pid; + if (pes) p->pes |= (tflags)(1 << filtn); + else { + off = MASKL*filtn; + p->pes &= ~((tflags) (1 << filtn) ); + for (i = 0; i < MASKL ; i++){ + p->mask[off+i] = mask[i]; + p->filt[off+i] = filt[i]; + } + } + p->set |= (tflags) (1 << filtn); + return 0; +} + +void clear_trans_filt(trans *p,int filtn) +{ + int i; + + p->set &= ~((tflags) (1 << filtn) ); + p->pes &= ~((tflags) (1 << filtn) ); + p->is_full &= ~((tflags) (1 << filtn) ); + p->pes_start &= ~((tflags) (1 << filtn) ); + p->pes_started &= ~((tflags) (1 << filtn) ); + + for (i = MASKL*filtn; i < MASKL*(filtn+1) ; i++){ + p->mask[i] = 0; + p->filt[i] = 0; + } + p->sec[filtn].found = 0; + p->sec[filtn].length = 0; +} + +int filt_is_set(trans *p, int filtn) +{ + if (p->set & ((tflags)(1 << filtn))) return 1; + return 0; +} + +int pes_is_set(trans *p, int filtn) +{ + if (p->pes & ((tflags)(1 << filtn))) return 1; + return 0; +} + +int pes_is_started(trans *p, int filtn) +{ + if (p->pes_started & ((tflags)(1 << filtn))) return 1; + return 0; +} + +int pes_is_start(trans *p, int filtn) +{ + if (p->pes_start & ((tflags)(1 << filtn))) return 1; + return 0; +} + +int filt_is_ready(trans *p,int filtn) +{ + if (p->is_full & ((tflags)(1 << filtn))) return 1; + return 0; +} + +void trans_filt(uint8_t *buf, int count, trans *p) +{ + int c=0; + //fprintf(stderr,"trans_filt\n"); + + + while (c < count && p->found <1 ){ + if ( buf[c] == 0x47) p->found = 1; + c++; + p->packet[0] = 0x47; + } + if (c == count) return; + + while( c < count && p->found < 188 && p->found > 0 ){ + p->packet[p->found] = buf[c]; + c++; + p->found++; + } + if (p->found == 188){ + p->found = 0; + tfilter(p); + } + + if (c < count) trans_filt(buf+c,count-c,p); +} + + +void tfilter(trans *p) +{ + int l,c; + int tpid; + uint8_t flag,flags; + uint8_t adapt_length = 0; + uint8_t cpid[2]; + + + // fprintf(stderr,"tfilter\n"); + + cpid[0] = p->packet[1]; + cpid[1] = p->packet[2]; + tpid = get_pid(cpid); + + if ( p->packet[1]&0x80){ + fprintf(stderr,"Error in TS for PID: %d\n", + tpid); + } + + flag = cpid[0]; + flags = p->packet[3]; + + if ( flags & ADAPT_FIELD ) { + // adaption field + adapt_length = p->packet[4]; + } + + c = 5 + adapt_length - (int)(!(flags & ADAPT_FIELD)); + if (flags & PAYLOAD){ + for ( l = 0; l < MAXFILT ; l++){ + if ( filt_is_set(p,l) ) { + if ( p->pid[l] == tpid) { + if ( pes_is_set(p,l) ){ + if (cpid[0] & PAY_START){ + p->pes_started |= + (tflags) + (1 << l); + p->pes_start |= + (tflags) + (1 << l); + } else { + p->pes_start &= ~ + ((tflags) + (1 << l)); + } + pes_filter(p,l,c); + } else { + sec_filter(p,l,c); + } + } + } + } + } +} + + +void pes_filter(trans *p, int filtn, int off) +{ + int count,c; + uint8_t *buf; + + if (filtn < 0 || filtn >= MAXFILT) return; + + count = 188 - off; + c = 188*filtn; + buf = p->packet+off; + if (pes_is_started(p,filtn)){ + p->is_full |= (tflags) (1 << filtn); + memcpy(p->transbuf+c,buf,count); + p->transcount[filtn] = count; + } +} + +section *get_filt_sec(trans *p, int filtn) +{ + section *sec; + + sec = &p->sec[filtn]; + p->is_full &= ~((tflags) (1 << filtn) ); + return sec; +} + +int get_filt_buf(trans *p, int filtn,uint8_t **buf) +{ + *buf = p->transbuf+188*filtn; + p->is_full &= ~((tflags) (1 << filtn) ); + return p->transcount[filtn]; +} + + + + +void sec_filter(trans *p, int filtn, int off) +{ + int i,j; + int error; + int count,c; + uint8_t *buf, *secbuf; + section *sec; + + // fprintf(stderr,"sec_filter\n"); + + if (filtn < 0 || filtn >= MAXFILT) return; + + count = 188 - off; + c = 0; + buf = p->packet+off; + sec = &p->sec[filtn]; + secbuf = sec->payload; + if(!filt_is_ready(p,filtn)){ + p->is_full &= ~((tflags) (1 << filtn) ); + sec->found = 0; + sec->length = 0; + } + + if ( !sec->found ){ + c = buf[c]+1; + if (c >= count) return; + sec->id = buf[c]; + secbuf[0] = buf[c]; + c++; + sec->found++; + sec->length = 0; + } + + while ( c < count && sec->found < 3){ + secbuf[sec->found] = buf[c]; + c++; + sec->found++; + } + if (c == count) return; + + if (!sec->length && sec->found == 3){ + sec->length |= ((secbuf[1] & 0x0F) << 8); + sec->length |= (secbuf[2] & 0xFF); + } + + while ( c < count && sec->found < sec->length+3){ + secbuf[sec->found] = buf[c]; + c++; + sec->found++; + } + + if ( sec->length && sec->found == sec->length+3 ){ + error=0; + for ( i = 0; i < MASKL; i++){ + if (i > 0 ) j=2+i; + else j = 0; + error += (sec->payload[j]&p->mask[MASKL*filtn+i])^ + (p->filt[MASKL*filtn+i]& + p->mask[MASKL*filtn+i]); + } + if (!error){ + p->is_full |= (tflags) (1 << filtn); + } + if (buf[0]+1 < c ) c=count; + } + + if ( c < count ) sec_filter(p, filtn, off); + +} + + +extern int errno; +const char * strerrno (void) +{ + return strerror(errno); +} diff --git a/pidfilter.h b/pidfilter.h new file mode 100644 index 0000000..558a3ca --- /dev/null +++ b/pidfilter.h @@ -0,0 +1,92 @@ +/* + * dvb-mpegtools for the Siemens Fujitsu DVB PCI card + * + * Copyright (C) 2000, 2001 Marcus Metzler + * for convergence integrated media GmbH + * Copyright (C) 2002 Marcus Metzler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + + * The author can be reached at mocm@metzlerbros.de, + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifndef __PIDFILTER_H +#define __PIDFILTER_H + +#define MAX_PLENGTH 0xFFFF + + typedef struct sectionstruct { + int id; + int length; + int found; + uint8_t payload[4096+3]; + } section; + + + typedef uint32_t tflags; +#define MAXFILT 32 +#define MASKL 16 + typedef struct trans_struct { + int found; + uint8_t packet[188]; + uint16_t pid[MAXFILT]; + uint8_t mask[MAXFILT*MASKL]; + uint8_t filt[MAXFILT*MASKL]; + uint8_t transbuf[MAXFILT*188]; + int transcount[MAXFILT]; + section sec[MAXFILT]; + tflags is_full; + tflags pes_start; + tflags pes_started; + tflags pes; + tflags set; + } trans; + + + void init_trans(trans *p); + int set_trans_filt(trans *p, int filtn, uint16_t pid, uint8_t *mask, + uint8_t *filt, int pes); + + void clear_trans_filt(trans *p,int filtn); + int filt_is_set(trans *p, int filtn); + int filt_is_ready(trans *p,int filtn); + + void trans_filt(uint8_t *buf, int count, trans *p); + void tfilter(trans *p); + void sec_filter(trans *p, int filtn, int off); + int get_filt_buf(trans *p, int filtn,uint8_t **buf); + section *get_filt_sec(trans *p, int filtn); + + + uint16_t get_pid(uint8_t *pid); + void pes_filter(trans *p, int filtn, int off); + +#endif //__PIDFILTER_H