mirror of
				https://github.com/rofafor/vdr-plugin-iptv.git
				synced 2023-10-10 11:37:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			297 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * sectionfilter.c: IPTV plugin for the Video Disk Recorder
 | 
						|
 *
 | 
						|
 * See the README file for copyright information and how to reach the author.
 | 
						|
 *
 | 
						|
 * $Id: sectionfilter.c,v 1.19 2008/02/17 19:18:47 rahrenbe Exp $
 | 
						|
 */
 | 
						|
 | 
						|
#include "sectionfilter.h"
 | 
						|
 | 
						|
cIptvSectionFilter::cIptvSectionFilter(int Index, int devInd,
 | 
						|
				       u_short Pid, u_char Tid, u_char Mask)
 | 
						|
: pusi_seen(0),
 | 
						|
  feedcc(0),
 | 
						|
  doneq(0),
 | 
						|
  secbuf(NULL),
 | 
						|
  secbufp(0),
 | 
						|
  seclen(0),
 | 
						|
  tsfeedp(0),
 | 
						|
  pid(Pid),
 | 
						|
  id(Index),
 | 
						|
  pipeName("")
 | 
						|
{
 | 
						|
  //debug("cIptvSectionFilter::cIptvSectionFilter(%d)\n", Index);
 | 
						|
  memset(secbuf_base, '\0', sizeof(secbuf_base));
 | 
						|
  memset(filter_value, '\0', sizeof(filter_value));
 | 
						|
  memset(filter_mask, '\0', sizeof(filter_mask));
 | 
						|
  memset(filter_mode, '\0', sizeof(filter_mode));
 | 
						|
  memset(maskandmode, '\0', sizeof(maskandmode));
 | 
						|
  memset(maskandnotmode, '\0', sizeof(maskandnotmode));
 | 
						|
 | 
						|
  SetPipeName(devInd);
 | 
						|
 | 
						|
  filter_value[0] = Tid;
 | 
						|
  filter_mask[0] = Mask;
 | 
						|
 | 
						|
  // Invert the filter
 | 
						|
  for (int i = 0; i < DMX_MAX_FILTER_SIZE; ++i)
 | 
						|
      filter_value[i] ^= 0xff;
 | 
						|
 | 
						|
  uint8_t mask, mode, local_doneq = 0;
 | 
						|
  for (int i = 0; i < DMX_MAX_FILTER_SIZE; ++i) {
 | 
						|
      mode = filter_mode[i];
 | 
						|
      mask = filter_mask[i];
 | 
						|
      maskandmode[i] = mask & mode;
 | 
						|
      local_doneq |= maskandnotmode[i] = mask & ~mode;
 | 
						|
      }
 | 
						|
  doneq = local_doneq ? 1 : 0;
 | 
						|
 | 
						|
  struct stat sb;
 | 
						|
  stat(pipeName, &sb);
 | 
						|
  if (S_ISFIFO(sb.st_mode))
 | 
						|
     unlink(pipeName);
 | 
						|
  int err = mknod(pipeName, 0644 | S_IFIFO, 0);
 | 
						|
  ERROR_IF_RET(err < 0, "mknod()", return);
 | 
						|
 | 
						|
  // Create descriptors
 | 
						|
  fifoDescriptor = open(pipeName, O_RDWR | O_NONBLOCK);
 | 
						|
  readDescriptor = open(pipeName, O_RDONLY | O_NONBLOCK);
 | 
						|
}
 | 
						|
 | 
						|
cIptvSectionFilter::~cIptvSectionFilter()
 | 
						|
{
 | 
						|
  //debug("cIptvSectionFilter::~cIptvSectionfilter(%d)\n", id);
 | 
						|
  close(fifoDescriptor);
 | 
						|
  close(readDescriptor);
 | 
						|
  unlink(pipeName);
 | 
						|
  fifoDescriptor = -1;
 | 
						|
  readDescriptor = -1;
 | 
						|
}
 | 
						|
 | 
						|
void cIptvSectionFilter::SetPipeName(int deviceIndex)
 | 
						|
{
 | 
						|
  pipeName = cString::sprintf(IPTV_FILTER_FILENAME, deviceIndex, id);
 | 
						|
}
 | 
						|
 | 
						|
int cIptvSectionFilter::GetReadDesc()
 | 
						|
{
 | 
						|
  return readDescriptor;
 | 
						|
}
 | 
						|
 | 
						|
inline uint16_t cIptvSectionFilter::section_length(const uint8_t *buf)
 | 
						|
{
 | 
						|
  return 3 + ((buf[1] & 0x0f) << 8) + buf[2];
 | 
						|
}
 | 
						|
 | 
						|
int cIptvSectionFilter::dmxdev_section_callback(const uint8_t *buffer1, size_t buffer1_len,
 | 
						|
                                                const uint8_t *buffer2, size_t buffer2_len,
 | 
						|
                                                enum dmx_success success)
 | 
						|
{
 | 
						|
  // See if there is data in the fifo
 | 
						|
  int retval = select_single_desc(fifoDescriptor, 0, false);
 | 
						|
  // There is no data in the fifo, more can be written
 | 
						|
  if (!retval) {
 | 
						|
#ifdef DEBUG_PRINTF
 | 
						|
     printf("id = %d, pid %d would now write %d data to buffer\n", id, pid, buffer1_len);
 | 
						|
     for (unsigned int i = 0; i < buffer1_len; ++i)
 | 
						|
         printf("0x%X ", buffer1[i]);
 | 
						|
     printf("\n");
 | 
						|
#endif
 | 
						|
     retval = write(fifoDescriptor, buffer1, buffer1_len);
 | 
						|
     ERROR_IF(retval < 0, "write()");
 | 
						|
     // Update statistics
 | 
						|
     AddSectionStatistic(retval, 1);
 | 
						|
     }
 | 
						|
#ifdef DEBUG_PRINTF
 | 
						|
  else if (retval)
 | 
						|
     printf("id %d pid %d data is already present\n", id, pid);
 | 
						|
#endif
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
void cIptvSectionFilter::demux_swfilter_section_new()
 | 
						|
{
 | 
						|
#ifdef DEMUX_SECTION_LOSS_LOG
 | 
						|
  if (secbufp < tsfeedp) {
 | 
						|
     int i, n = tsfeedp - secbufp;
 | 
						|
     /*
 | 
						|
      * Section padding is done with 0xff bytes entirely.
 | 
						|
      * Due to speed reasons, we won't check all of them
 | 
						|
      * but just first and last.
 | 
						|
      */
 | 
						|
     if (secbuf[0] != 0xff || secbuf[n - 1] != 0xff) {
 | 
						|
        printf("sectionfilter.c section ts padding loss: %d/%d\n",
 | 
						|
               n, tsfeedp);
 | 
						|
        printf("sectionfilter.c pad data:");
 | 
						|
        for (i = 0; i < n; ++i)
 | 
						|
	    printf(" %02x", secbuf[i]);
 | 
						|
        printf("\n");
 | 
						|
        }
 | 
						|
     }
 | 
						|
#endif
 | 
						|
  tsfeedp = secbufp = seclen = 0;
 | 
						|
  secbuf = secbuf_base;
 | 
						|
}
 | 
						|
 | 
						|
int cIptvSectionFilter::demux_swfilter_sectionfilter()
 | 
						|
{
 | 
						|
  uint8_t neq = 0;
 | 
						|
  int i;
 | 
						|
 | 
						|
  for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) {
 | 
						|
      uint8_t local_xor = filter_value[i] ^ secbuf[i];
 | 
						|
      if (maskandmode[i] & local_xor) {
 | 
						|
#ifdef DEBUG_PRINTF
 | 
						|
         printf("maskandmode discard\n");
 | 
						|
#endif
 | 
						|
         return 0;
 | 
						|
         }
 | 
						|
      neq |= maskandnotmode[i] & local_xor;
 | 
						|
      }
 | 
						|
 | 
						|
  if (doneq && !neq) {
 | 
						|
#ifdef DEBUG_PRINTF
 | 
						|
     printf("doneq discard, doneq = 0x%X, neq = 0x%X\n", doneq, !neq);
 | 
						|
#endif
 | 
						|
     return 0;
 | 
						|
     }
 | 
						|
  return dmxdev_section_callback(secbuf, seclen, NULL, 0, DMX_OK);
 | 
						|
}
 | 
						|
 | 
						|
inline int cIptvSectionFilter::demux_swfilter_section_feed()
 | 
						|
{
 | 
						|
  if (demux_swfilter_sectionfilter() < 0)
 | 
						|
     return -1;
 | 
						|
  seclen = 0;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int cIptvSectionFilter::demux_swfilter_section_copy_dump(const uint8_t *buf, uint8_t len)
 | 
						|
{
 | 
						|
  uint16_t limit, seclen_local, n;
 | 
						|
 | 
						|
  if (tsfeedp >= DMX_MAX_SECFEED_SIZE)
 | 
						|
     return 0;
 | 
						|
 | 
						|
  if (tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
 | 
						|
#ifdef DEMUX_SECTION_LOSS_LOG
 | 
						|
     printf("sectionfilter.c section buffer full loss: %d/%d\n",
 | 
						|
            tsfeedp + len - DMX_MAX_SECFEED_SIZE,
 | 
						|
            DMX_MAX_SECFEED_SIZE);
 | 
						|
#endif
 | 
						|
     len = DMX_MAX_SECFEED_SIZE - tsfeedp;
 | 
						|
     }
 | 
						|
 | 
						|
  if (len <= 0)
 | 
						|
     return 0;
 | 
						|
 | 
						|
  memcpy(secbuf_base + tsfeedp, buf, len);
 | 
						|
  tsfeedp += len;
 | 
						|
 | 
						|
  /*
 | 
						|
   * Dump all the sections we can find in the data (Emard)
 | 
						|
   */
 | 
						|
  limit = tsfeedp;
 | 
						|
  if (limit > DMX_MAX_SECFEED_SIZE)
 | 
						|
     return -1;	/* internal error should never happen */
 | 
						|
 | 
						|
  /* to be sure always set secbuf */
 | 
						|
  secbuf = secbuf_base + secbufp;
 | 
						|
 | 
						|
  for (n = 0; secbufp + 2 < limit; ++n) {
 | 
						|
      seclen_local = section_length(secbuf);
 | 
						|
      if (seclen_local <= 0 || seclen_local > DMX_MAX_SECTION_SIZE ||
 | 
						|
         seclen_local + secbufp > limit)
 | 
						|
         return 0;
 | 
						|
#ifdef DEBUG_PRINTF
 | 
						|
      printf("Non-mismatching seclen! %d, limit = %d\n", seclen_local, limit);
 | 
						|
#endif
 | 
						|
      seclen = seclen_local;
 | 
						|
      /* dump [secbuf .. secbuf+seclen) */
 | 
						|
      if (pusi_seen)
 | 
						|
         demux_swfilter_section_feed();
 | 
						|
#ifdef DEMUX_SECTION_LOSS_LOG
 | 
						|
      else
 | 
						|
         printf("sectionfilter.c pusi not seen, discarding section data\n");
 | 
						|
#endif
 | 
						|
      secbufp += seclen_local;	/* secbufp and secbuf moving together is */
 | 
						|
      secbuf += seclen_local;	/* redundant but saves pointer arithmetic */
 | 
						|
      }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
void cIptvSectionFilter::ProcessData(const uint8_t* buf)
 | 
						|
{
 | 
						|
  if (buf[0] != 0x47) {
 | 
						|
     error("Not TS packet: 0x%X\n", buf[0]);
 | 
						|
     return;
 | 
						|
     }
 | 
						|
  
 | 
						|
  // Stop if not the PID this filter is looking for
 | 
						|
  if (ts_pid(buf) != pid)
 | 
						|
     return;
 | 
						|
 | 
						|
  uint8_t count = payload(buf);
 | 
						|
 | 
						|
  if (count == 0)		/* count == 0 if no payload or out of range */
 | 
						|
     return;
 | 
						|
 | 
						|
  uint8_t p = 188 - count;	/* payload start */
 | 
						|
 | 
						|
  uint8_t cc = buf[3] & 0x0f;
 | 
						|
  int ccok = ((feedcc + 1) & 0x0f) == cc;
 | 
						|
  feedcc = cc;  
 | 
						|
 | 
						|
  int dc_i = 0;
 | 
						|
  if (buf[3] & 0x20) {
 | 
						|
     /* adaption field present, check for discontinuity_indicator */
 | 
						|
     if ((buf[4] > 0) && (buf[5] & 0x80))
 | 
						|
        dc_i = 1;
 | 
						|
     }
 | 
						|
 | 
						|
  if (!ccok || dc_i) {
 | 
						|
#ifdef DEMUX_SECTION_LOSS_LOG
 | 
						|
     printf("sectionfilter.c discontinuity detected %d bytes lost\n",
 | 
						|
            count);
 | 
						|
     /*
 | 
						|
      * those bytes under sume circumstances will again be reported
 | 
						|
      * in the following demux_swfilter_section_new
 | 
						|
      */
 | 
						|
#endif
 | 
						|
     /*
 | 
						|
      * Discontinuity detected. Reset pusi_seen = 0 to
 | 
						|
      * stop feeding of suspicious data until next PUSI=1 arrives
 | 
						|
      */
 | 
						|
     pusi_seen = 0;
 | 
						|
     demux_swfilter_section_new();
 | 
						|
     }
 | 
						|
 | 
						|
  if (buf[1] & 0x40) {
 | 
						|
     /* PUSI=1 (is set), section boundary is here */
 | 
						|
     if (count > 1 && buf[p] < count) {
 | 
						|
#ifdef DEBUG_PRINTF
 | 
						|
        printf("Valid section\n");
 | 
						|
#endif
 | 
						|
        const uint8_t *before = &buf[p + 1];
 | 
						|
        uint8_t before_len = buf[p];
 | 
						|
        const uint8_t *after = &before[before_len];
 | 
						|
        uint8_t after_len = (count - 1) - before_len;
 | 
						|
 | 
						|
        demux_swfilter_section_copy_dump(before, before_len);
 | 
						|
        /* before start of new section, set pusi_seen = 1 */
 | 
						|
        pusi_seen = 1;
 | 
						|
        demux_swfilter_section_new();
 | 
						|
        demux_swfilter_section_copy_dump(after, after_len);
 | 
						|
        }
 | 
						|
#ifdef DEMUX_SECTION_LOSS_LOG
 | 
						|
     else if (count > 0)
 | 
						|
        printf("sectionfilter.c PUSI=1 but %d bytes lost\n", count);
 | 
						|
#endif
 | 
						|
     }
 | 
						|
  else {
 | 
						|
     /* PUSI=0 (is not set), no section boundary */
 | 
						|
     demux_swfilter_section_copy_dump(&buf[p], count);
 | 
						|
     }
 | 
						|
}
 |