File:  [DVB] / multiplexer / splitts.c
Revision 1.24: download - view: text, annotated - select for diffs
Thu Oct 17 08:37:06 2013 UTC (10 years, 7 months ago) by oskar
Branches: MAIN
CVS tags: HEAD
v1.1.8

/*
 * ISO 13818 stream multiplexer
 * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin
 * Author: Oskar Schirmer (schirmer@scara.com)
 *
 * 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
 */

/*
 * Module:  Split TS
 * Purpose: Split a transport stream.
 *
 * This module examines a transport stream and collects the packets to
 * the input stream buffers. PSI data is extracted (and stored in the
 * files pat structures), descriptors are copied to the corresponding
 * mapstreams.
 */

#include "global.h"
#include "error.h"
#include "input.h"
#include "splice.h"
#include "ts.h"
#include "pes.h"
#include "descref.h"
#include "splitpes.h"
#include "splitts.h"
#include "crc32.h"

static byte pusi, afcc, afflg1;
static int paylen;

/* Skip in input raw data buffer for TS-syncbyte.
 * Precondition: f!=NULL
 * Postcondition: if found: f->data.out indicates the syncbyte.
 * Return: TRUE if found, FALSE otherwise
 */
static boolean ts_skip_to_syncbyte (file_descr *f)
{
  int i, l, k;
  boolean r = FALSE;
  l = k = list_size (f->data);
  i = f->data.out;
  while ((l > 0)
      && ((f->data.ptr[i] != TS_SYNC_BYTE) || (r = TRUE, FALSE))) {
    list_incr (i,f->data,1);
    l -= 1;
  }
  k -= l;
  if (k > 0) {
    warn (LWAR,"Skipped",ETST,1,1,k);
    f->skipped += k;
    f->total += k;
    list_incr (f->data.out,f->data,k);
  }
  return (r);
}

/* Determine the PID of a TS packet and other details.
 * Procondition: d!=NULL points to a packet.
 * Postcondition: Set global "Payload Unit Start Indicator", "Adaption Field
 * Control & Continuity Counter", "Adataption Field Flags 1".
 * Return: 13 bit PID.
 */
static int ts_packet_headinfo (refr_data *d)
{
  int i, l;
  i = d->out;
  list_incr (i,*d,TS_PACKET_PID);
  l = (pusi = d->ptr[i]) & 0x1F;
  list_incr (i,*d,1);
  l = (l << 8) | d->ptr[i];
  list_incr (i,*d,1);
  if (!((afcc = d->ptr[i]) & TS_AFC_BOTH)) {
    l = TS_PID_NULL;
  } else {
    if (afcc & TS_AFC_ADAPT) {
      byte aflen;
      list_incr (i,*d,1);
      if ((aflen = d->ptr[i]) == 0) {
        afflg1 = 0x00;
      } else {
        list_incr (i,*d,1);
        afflg1 = d->ptr[i];
      }
      paylen = TS_PACKET_SIZE - TS_PACKET_FLAGS1 - aflen;
    } else {
      paylen = TS_PACKET_SIZE - TS_PACKET_HEADSIZE;
    }
  }
  warn (LSEC,"Packet PID",ETST,2,1,l);
  warn (LDEB,"Packet PID",ETST,afcc,afflg1,paylen);
  return (l);
}

/* Extract adaption field from a TS packet.
 * Distribute the information as needed (to s, s->ctrl, etc).
 * Precondition: f!=NULL, adf indicates a packet in f->data, s!=NULL is the
 * destined data stream, m!=NULL the map stream, afcc and afflg1 set.
 */
static void ts_adaption_field (file_descr *f,
    int adf,
    stream_descr *s,
    stream_descr *m)
{
  warn (LDEB,"AdaptF",ETST,11,adf,afcc);
  if (afcc & TS_AFC_ADAPT) {
    list_incr (adf,f->data,TS_PACKET_FLAGS1+1);
    if (afflg1 & TS_ADAPT_DISCONTI) {
    }
    if (afflg1 & TS_ADAPT_RANDOMAC) {
    }
    if (afflg1 & TS_ADAPT_PRIORITY) {
    }
    if (afflg1 & TS_ADAPT_PCRFLAG) {
      clockref *pcr;
      long b;
      byte a;
      pcr = &s->ctrl.ptr[s->ctrl.in].pcr;
      a = f->data.ptr[adf];
      list_incr (adf,f->data,1);
      pcr->ba33 = (a >> 7) & 1;
      b = (a << 8) | f->data.ptr[adf];
      list_incr (adf,f->data,1);
      b = (b << 8) | f->data.ptr[adf];
      list_incr (adf,f->data,1);
      b = (b << 8) | f->data.ptr[adf];
      list_incr (adf,f->data,1);
      a = f->data.ptr[adf];
      pcr->base = (b << 1) | ((a >> 7) & 1);
      marker_check (a,0x7E,0x7E);
      list_incr (adf,f->data,1);
      pcr->ext = ((a & 1) << 8) | f->data.ptr[adf];
      warn (LINF,"PCR",ETST,10,pcr->ba33,pcr->base);
      pcr->valid = TRUE;
      list_incr (adf,f->data,1);
/* attention ! what if it is not PCR_PID ? xxx */
      if (S_ISREG (f->st_mode)) {
        cref2msec (&m->u.m.conv, *pcr, &m->u.m.msectime); 
      } else {
        cref2msec (&m->u.m.conv, *pcr, &m->u.m.msectime); 
      }
    }
    if (afflg1 & TS_ADAPT_OPCRFLAG) {
      clockref *opcr;
      long b;
      byte a;
      opcr = &s->ctrl.ptr[s->ctrl.in].opcr;
      a = f->data.ptr[adf];
      list_incr (adf,f->data,1);
      opcr->ba33 = (a >> 7) & 1;
      b = (a << 8) | f->data.ptr[adf];
      list_incr (adf,f->data,1);
      b = (b << 8) | f->data.ptr[adf];
      list_incr (adf,f->data,1);
      b = (b << 8) | f->data.ptr[adf];
      list_incr (adf,f->data,1);
      a = f->data.ptr[adf];
      opcr->base = (b << 1) | ((a >> 7) & 1);
      marker_check (a,0x7E,0x7E);
      list_incr (adf,f->data,1);
      opcr->ext = ((a & 1) << 8) | f->data.ptr[adf];
      warn (LINF,"OPCR",ETST,10,opcr->ba33,opcr->base);
      opcr->valid = TRUE;
      s->u.d.has_opcr = TRUE;
      list_incr (adf,f->data,1);
    }
    if (afflg1 & TS_ADAPT_SPLICING) {
      list_incr (adf,f->data,1);
    }
    if (afflg1 & TS_ADAPT_TPRIVATE) {
      byte tpdl;
      tpdl = f->data.ptr[adf];
      list_incr (adf,f->data,tpdl+1);
    }
    if (afflg1 & TS_ADAPT_EXTENSIO) {
      byte afflg2, afel;
      afel = f->data.ptr[adf];
      list_incr (adf,f->data,1);
      afflg2 = f->data.ptr[adf];
      list_incr (adf,f->data,1);
      if (afflg2 & TS_ADAPT2_LTWFLAG) {
        list_incr (adf,f->data,2);
      }
      if (afflg2 & TS_ADAPT2_PIECEWRF) {
        list_incr (adf,f->data,3);
      }
      if (afflg2 & TS_ADAPT2_SEAMLESS) {
        list_incr (adf,f->data,5);
      }
    }
  }
}

/* Parse one TS packet with given PID.
 * Depending on the actual state (c->length) and the contents of the packet,
 * provide the data into the stream and possibly complete a PES package.
 * c->length keeps the progress of data acquisition, as follows:
 *  =0: initial, waiting for payload unit start indicator
 *  <-2, increasing: not yet enough data to evaluate the PES header
 *  =-2: evaluate the PES packet length field
 *  =-1: acquiring packet with unspecified length
 *  >0, decreasing: amount of data still missing
 *  =0: final, close PES packet, start new one
 * Precondition: f!=NULL.
 * Return: TRUE if something was processed, FALSE if no data/space available
 */
static boolean ts_data_stream (file_descr *f,
    int pid)
{
  stream_descr *s;
  ctrl_buffer *c;
  int i, fdo, sdi, adf;
  warn (LDEB,"Data Packet",ETST,3,0,pid);
  s = ts_file_stream (f,pid);
  if (s != NULL) {
    if (!list_full (s->ctrl)) {
      c = &s->ctrl.ptr[s->ctrl.in];
      if (c->length == -1) {
        if (pusi & TS_UNIT_START) {
          c->length = s->data.in - c->index;
          warn (LINF,"Closed unbound packet",ETST,3,5,c->length);
          f->payload += c->length;
          c->sequence = f->sequence++;
          c->scramble = 0;
          c->msecread = msec_now ();
          c->msecpush = s->u.d.mapstream->u.m.msectime;
          list_incr (s->ctrl.in,s->ctrl,1);
          c = &s->ctrl.ptr[s->ctrl.in];
          c->length = 0;
          c->pcr.valid = FALSE;
          c->opcr.valid = FALSE;
        }
      }
      if (c->length == 0) {
        if (pusi & TS_UNIT_START) {
          if (list_free (s->data) >=
              (2*(PES_HEADER_SIZE-1+TS_PACKET_SIZE-TS_PACKET_HEADSIZE)-1)) {
            if ((PES_HEADER_SIZE-1+TS_PACKET_SIZE-TS_PACKET_HEADSIZE)
                > list_freeinend (s->data)) {
              s->data.in = 0;
            }
            c->index = s->data.in;
            c->length = -2 - PES_HEADER_SIZE;
          } else {
            return (FALSE);
          }
        } else {
          f->skipped += TS_PACKET_SIZE;
          list_incr (f->data.out,f->data,TS_PACKET_SIZE);
          f->total += TS_PACKET_SIZE;
          return (TRUE);
        }
      }
      sdi = s->data.in;
      fdo = adf = f->data.out;
      list_incr (fdo,f->data,TS_PACKET_SIZE - paylen);
      if (c->length == -1) {
        if (list_freecachedin (s->data,sdi) >= paylen) {
          i = list_freeinendcachedin (s->data,sdi);
          if (i <= paylen) {
            if (list_freecachedin (s->data,sdi) >=
                (i + (sdi - c->index) + paylen)) {
              sdi -= c->index;
              memmove (&s->data.ptr[0],&s->data.ptr[c->index],sdi);
              c->index = 0;
            } else {
              return (FALSE);
            }
          }
          while (paylen > 0) {
            i = MAX_DATA_RAWB - fdo;
            if (i > paylen) {
              i = paylen;
            }
            memcpy (&s->data.ptr[sdi],&f->data.ptr[fdo],i);
            list_incr (sdi,s->data,i);
            list_incr (fdo,f->data,i);
            paylen -= i;
          }
        } else {
          return (FALSE);
        }
      }
      if (c->length < -2) {
        c->length += paylen;
        while (paylen > 0) {
          i = MAX_DATA_RAWB - fdo;
          if (i > paylen) {
            i = paylen;
          }
          memcpy (&s->data.ptr[sdi],&f->data.ptr[fdo],i);
          list_incr (sdi,s->data,i);
          list_incr (fdo,f->data,i);
          paylen -= i;
        }
        if (c->length > -2) {
          c->length = -2;
        }
      }
      if (c->length == -2) {
        if ((s->data.ptr[c->index] != 0x00)
         || (s->data.ptr[c->index+1] != 0x00)
         || (s->data.ptr[c->index+2] != 0x01)) {
          warn (LWAR,"Payload not good PES",ETST,3,3,0);
          c->length = 0;
          return (TRUE);
        }
        i = (s->data.ptr[c->index+PES_PACKET_LENGTH] << 8)
           | s->data.ptr[c->index+PES_PACKET_LENGTH+1];
        if (i == 0) {
          warn (LSEC,"Payload length 0",ETST,3,4,0);
          c->length = -1;
        } else {
          if (i > list_freeinendcachedin (s->data,c->index) - PES_HEADER_SIZE) {
            if (list_freecachedin (s->data,sdi) <
                  (2 * (i + PES_HEADER_SIZE) - (sdi - c->index) - 1)) {
              return (FALSE);
            } else {
              sdi -= c->index;
              memmove (&s->data.ptr[0],&s->data.ptr[c->index],sdi);
              c->index = 0;
            }
          } else {
            if (list_freecachedin (s->data,sdi) <
                  (i + PES_HEADER_SIZE - (sdi - c->index))) {
              return (FALSE);
            }
          }
          c->length = i + PES_HEADER_SIZE - (sdi - c->index);
          if (c->length < 0) {
            warn (LWAR,"Too much payload",ETST,3,1,c->length);
            c->length = 0;
          }
        }
      }
      if ((c->length > 0)
       && (paylen > 0)) {
        if (paylen > c->length) {
          warn (LWAR,"Too much payload",ETST,3,2,c->length);
          paylen = c->length;
        }
        c->length -= paylen;
        while (paylen > 0) {
          i = MAX_DATA_RAWB - fdo;
          if (i > paylen) {
            i = paylen;
          }
          memcpy (&s->data.ptr[sdi],&f->data.ptr[fdo],i);
          list_incr (sdi,s->data,i);
          list_incr (fdo,f->data,i);
          paylen -= i;
        }
      }
      s->data.in = sdi;
      f->data.out = fdo;
      ts_adaption_field (f,adf,s,s->u.d.mapstream);
      if (c->length == 0) {
        c->length = s->data.in - c->index;
        f->payload += c->length;
        c->sequence = f->sequence++;
        c->scramble = 0;
        c->msecread = msec_now ();
        c->msecpush = s->u.d.mapstream->u.m.msectime;
        list_incr (s->ctrl.in,s->ctrl,1);
        c = &s->ctrl.ptr[s->ctrl.in];
        c->length = 0;
        c->pcr.valid = FALSE;
        c->opcr.valid = FALSE;
      }
    } else {
      return (FALSE);
    }
  } else {
    list_incr (f->data.out,f->data,TS_PACKET_SIZE);
  }
  f->total += TS_PACKET_SIZE;
  return (TRUE);
}

/* Process changes that arise from a new PMT.
 * Match the new PMT with the existing PAT/PMT and adjust this to
 * reflect the changes in the TS. Try to keep PIDs etc stable.
 * Precondition: f!=NULL, new!=NULL the partial new PMT
 */
static void remap_new_program (file_descr *f,
    pmt_descr *new)
{
  pmt_descr* p;
  int i, j, k;
  byte *d;
  struct {
    boolean tnew : 1;
    boolean tpat : 1;
  } treated[MAX_PMTSTREAMS];
  warn (LINF,"Remap",ETST,7,new->descrlen,new->pmt_pid);
  p = f->u.ts.pat;
  while ((p != NULL) && (p->programnumber != new->programnumber)) {
    p = p->next;
  }
  warn (LDEB,"Remap 2",ETST,p?p->streams:0,(long)f->u.ts.pat,p);
  if (p != NULL) {
  warn (LDEB,"Remap 2a",ETST,p->pmt_version,new->pmt_version,new);
    if (p->pmt_version == new->pmt_version) {
      /* check more here, or fall through ? */
      p->pat_section = new->pat_section; /* announce presence */
      return;
    }
  } else {
    if ((p = malloc (sizeof(pmt_descr))) == NULL) {
      warn (LERR,"PAT malloc failed",ETST,7,1,sizeof(pmt_descr));
      return;
    }
    p->next = f->u.ts.pat;
    p->programnumber = new->programnumber;
    p->streams = 0;
    f->u.ts.pat = p;
    warn (LIMP,"Remap PAT",ETST,7,new->pat_section,new->pmt_pid);
  }
  p->pat_section = new->pat_section;
  p->pmt_version = new->pmt_version;
  p->pcr_pid = new->pcr_pid;
  if (ts_file_stream(f,new->pmt_pid) == NULL) {
    if ((ts_file_stream(f,new->pmt_pid) =
        input_openstream (f,new->pmt_pid,0,0,sd_map,NULL)) == NULL) {
      p->pat_section = -1;
      return;
    }
  }
  p->pmt_pid = new->pmt_pid;
  memset (&treated[0],0,sizeof(treated));
  i = new->streams;
  while (--i >= 0) {
    j = p->streams;
    warn (LDEB,"Remap 3",ETST,0,i,j);
    while ((--j >= 0)
        && (p->streamtype[j] != new->streamtype[i])
        && (p->stream[j] != new->stream[i])) {
    }
    if (j >= 0) {
      treated[j].tpat = TRUE;
      treated[i].tnew = TRUE;
      warn (LIMP,"Remap Match",ETST,7,new->streamtype[i],new->stream[i]);
    }
  }
  i = new->streams;
  while (--i >= 0) {
    if (!treated[i].tnew) {
      j = p->streams;
      while ((--j >= 0)
          && (treated[j].tpat)
          && (p->streamtype[j] != new->streamtype[i])) {
      }
      if (j >= 0) {
        treated[j].tpat = TRUE;
        warn (LIMP,"Remap Move",ETST,7,p->stream[j],new->stream[i]);
        warn (LSEC,"Remap Move",ETST,7,p->streamtype[j],new->streamtype[i]);
        if (ts_file_stream(f,p->stream[j]) != NULL) {
          k = ts_file_stream(f,p->stream[j])->u.d.progs;
          while (--k >= 0) {
            ts_file_stream(f,new->stream[i]) =
              connect_streamprog (
                f,ts_file_stream(f,p->stream[j])->u.d.pdescr[k]->program_number,
                new->stream[i],
                ts_file_stream(f,p->stream[j])->stream_id,new->streamtype[i],
                ts_file_stream(f,new->stream[i]),
                ts_file_stream(f,new->pmt_pid),FALSE);
          }
          if (ts_file_stream(f,new->stream[i]) != NULL) {
            ts_file_stream(f,new->stream[i])->u.d.trigger = TRUE;
          }
          ts_file_stream(f,p->stream[j])->endaction = ENDSTR_KILL;
        } else {
          /* was not used -> will not use */
        }
      } else {
        warn (LIMP,"Remap New",ETST,7,new->streamtype[i],new->stream[i]);
      }
    }
  }
  j = p->streams;
  while (--j >= 0) {
    if (!(treated[j].tpat)) {
      warn (LIMP,"Remap Close",ETST,7,p->streamtype[j],p->stream[j]);
      ts_file_stream (f,p->stream[j])->endaction = ENDSTR_KILL;
    }
  }
  p->streams = new->streams;
  memcpy (p->stream,new->stream,p->streams * sizeof(p->stream[0]));
  memcpy (p->streamtype,new->streamtype,p->streams * sizeof(p->streamtype[0]));
  p->descrlen = i = new->descrlen;
  if (i != 0) {
    memcpy (p->elemdescr,new->elemdescr,i);
    d = &p->elemdescr[0];
    j = ((d[0] & 0x0F) << 8) + d[1];
    i -= (j+2);
    if (i >= 0) {
      d += 2;
      alloc_descriptor (ts_file_stream(f,p->pmt_pid),p->pmt_pid,
          p->programnumber,p->pmt_version);
      while (j > 1) {
        d = put_descriptor_s (d,ts_file_stream(f,p->pmt_pid),&j);
      }
      if (j != 0) {
        warn (LWAR,"PAT descr error",ETST,7,3,j);
      } else {
        finish_descriptor (ts_file_stream(f,p->pmt_pid));
        while (i >= TS_PMTELEM_SIZE) {
          k = ((d[1] & 0x1F) << 8) + d[2];
          j = ((d[3] & 0x0F) << 8) + d[4];
          i -= (j+TS_PMTELEM_SIZE);
          if (i >= 0) {
            d += TS_PMTELEM_SIZE;
            alloc_descriptor (ts_file_stream(f,p->pmt_pid),k,
                p->programnumber,p->pmt_version);
            while (j > 1) {
              d = put_descriptor_s (d,ts_file_stream(f,p->pmt_pid),&j);
            }
            if (j != 0) {
              warn (LWAR,"PAT descr error",ETST,7,5,j);
              i = -1;
            } else {
              finish_descriptor (ts_file_stream(f,p->pmt_pid));
            }
          }
        }
        if (i != 0) {
          warn (LWAR,"PAT descr error",ETST,7,4,i);
        }
      }
    } else {
      warn (LWAR,"PAT descr error",ETST,7,2,i);
    }
  }
}

/* Unlink stream in an old program that will be released.
 * Mark the corresponding streams to be closed as they cease.
 * Precondition: f!=NULL, old!=NULL
 */
static void unmap_old_program (file_descr *f,
    pmt_descr *old)
{
  int i;
  warn (LIMP,"Unmap",ETST,8,old->descrlen,old->pmt_pid);
  i = old->streams;
  while (--i >= 0) {
    if (ts_file_stream (f,old->stream[i]) != NULL) {
      warn (LIMP,"Unmap Close",ETST,8,old->streamtype[i],old->stream[i]);
      ts_file_stream (f,old->stream[i])->endaction = ENDSTR_KILL;
    }
  }
  if (ts_file_stream (f,old->pmt_pid) != NULL) {
    ts_file_stream (f,old->pmt_pid)->endaction = ENDSTR_CLOSE;
  }
}

/* Release old programs that are no longer valid.
 * Old programs are marked with pat_section < 0.
 * If f!=NULL, then also unmap all the old programs (used with current pat).
 * Precondition: pp!=NULL.
 */
static void release_old_progs (file_descr *f,
    pmt_descr **pp)
{
  pmt_descr *p;
  p = *pp;
  while (p != NULL) {
    if (p->pat_section < 0) {
      if (f != NULL) {
        unmap_old_program (f,p);
      }
      *pp = p->next;
      free (p);
    } else {
      pp = &p->next;
    }
    p = *pp;
  }
}

static void eval_pat_section (file_descr *f,
    stream_descr *s,
    int seclen,
    int tsid,
    int versionnb,
    boolean curni,
    int sectionnb,
    int lastsecnb)
{
  pmt_descr *p;
  byte *d;
  int i, l;
  warn (LINF,"eval PAT",ETST,5,seclen,tsid);
  if (curni) {
    if (versionnb != f->u.ts.newpat_version) {
      f->u.ts.newpat_version = versionnb;
      p = f->u.ts.newpat;
      while (p != NULL) {
        p->pat_section = -1;
        p = p->next;
      }
    }
    d = &s->u.m.psi_data[TS_SECTIONHEAD];
    l = seclen - TS_PATSECT_SIZE;
    while (l >= TS_PATPROG_SIZE) {
      i = (d[0] << 8) + d[1];
      if (i != 0) {
        p = f->u.ts.newpat;
        while ((p != NULL) && (p->programnumber != i)) {
          p = p->next;
        }
        warn (LDEB,"eval PAT 2",ETST,i,(long)f->u.ts.newpat,p);
        if (p == NULL) {
          if ((p = malloc (sizeof(pmt_descr))) == NULL) {
            warn (LERR,"PAT malloc failed",ETST,5,3,sizeof(pmt_descr));
            return;
          }
          p->next = f->u.ts.newpat;
          p->pmt_version = 0xFF;
          p->programnumber = i;
          p->pcr_pid = 0;
          p->streams = 0;
          p->descrlen = 0;
          f->u.ts.newpat = p;
        }
        p->pat_section = sectionnb;
        p->pmt_pid = ((d[2] & 0x1F) << 8) + d[3];
      }
      d += TS_PATPROG_SIZE;
      l -= TS_PATPROG_SIZE;
    }
    if (l != 0) {
      warn (LWAR,"PAT data error",ETST,5,2,seclen);
    } else {
      if (sectionnb == lastsecnb) {
        p = f->u.ts.pat;
        while (p != NULL) {
          warn (LDEB,"eval PAT 3",ETST,p->pcr_pid,p->pat_section,p);
          p->pat_section = -1;
          p = p->next;
        }
        release_old_progs (NULL,&f->u.ts.newpat);
        p = f->u.ts.newpat;
        while (p != NULL) {
          remap_new_program (f,p);
          p = p->next;
        }
        release_old_progs (f,&f->u.ts.pat);
      }
    }
  } else {
    if (versionnb == f->u.ts.pat_version) {
      warn (LWAR,"Same PAT version w/o cni",ETST,5,1,versionnb);
    }
  }
}

static void eval_cat_section (file_descr *f,
    stream_descr *s,
    int seclen,
    int versionnb,
    boolean curni,
    int sectionnb,
    int lastsecnb)
{
}

static void eval_pmt_section (file_descr *f,
    stream_descr *s,
    int seclen,
    int prognb,
    int versionnb,
    boolean curni)
{
  pmt_descr *p;
  int i, epid, esil;
  byte *d;
  byte styp;
  warn (LINF,"eval PMT",ETST,6,seclen,prognb);
  p = f->u.ts.newpat;
  while ((p != NULL) && (p->programnumber != prognb)) {
    p = p->next;
  }
  if (curni) {
    if (p == NULL) {
      warn (LWAR,"Program not in PAT",ETST,6,1,prognb);
    } else {
      p->pmt_version = versionnb;
      p->pcr_pid = ((s->u.m.psi_data[TS_PMT_PCRPID] & 0x1F) << 8)
                 + s->u.m.psi_data[TS_PMT_PCRPID+1];
      p->streams = 0;
      p->descrlen = seclen-TS_PMT_PILEN-CRC_SIZE;
      memcpy (&p->elemdescr[0],&s->u.m.psi_data[TS_PMT_PILEN],
              seclen-TS_PMT_PILEN-CRC_SIZE);
      i = ((s->u.m.psi_data[TS_PMT_PILEN] & 0x0F) << 8)
        + s->u.m.psi_data[TS_PMT_PILEN+1];
      d = &s->u.m.psi_data[TS_PMTSECTHEAD+i];
      i = seclen - i - TS_PMTSECT_SIZE;
      while (i >= TS_PMTELEM_SIZE) {
        i -= TS_PMTELEM_SIZE;
        styp = *d++;
        epid = (*d++ & 0x1F) << 8;
        epid = epid | *d++;
        if ((epid >= TS_PID_LOWEST) && (epid <= TS_PID_HIGHEST)) {
          p->stream[p->streams] = epid;
          p->streamtype[p->streams] = styp;
          p->streams += 1;
          if ((ts_file_stream(f,epid) != NULL)
           && (ts_file_stream(f,epid)->streamdata == sd_data)
           && (ts_file_stream(f,epid)->u.d.mapstream == ts_file_stream(f,0))) {
            ts_file_stream(f,epid)->u.d.mapstream = s;
            warn (LIMP,"PMT stream caught",ETST,6,5,epid);
          }
          esil = (*d++ & 0x0F) << 8;
          esil = esil | *d++;
          d += esil;
          i -= esil;
        } else {
          warn (LWAR,"PMT data error",ETST,6,3,epid);
          i = -1;
        }
      }
      if (i != 0) {
        warn (LWAR,"PMT data error",ETST,6,2,i);
        p->streams = 0;
      } else {
        remap_new_program (f,p);
        release_old_progs (f,&f->u.ts.pat);
      }
    }
  } else {
    if ((p != NULL) && (versionnb == p->pmt_version)) {
      warn (LWAR,"PMT next same version",ETST,6,4,versionnb);
    }
  }
}

/* Extract a partial PSI section from a TS packet.
 * If complete, process its contents (via eval_*_section)
 * Precondition: f!=NULL
 * Return: TRUE if something was processed, FALSE if no data/space available
 */
static boolean ts_psi_table_section (file_descr *f,
    int pid,
    int tableid)
{
  stream_descr *s;
  int i, b, adf;
  int seclen;
  boolean complete;
  warn (LDEB,"PSI",ETST,4,pid,tableid);
  adf = f->data.out;
  list_incr (f->data.out,f->data,TS_PACKET_SIZE - paylen);
  s = ts_file_stream (f,pid);
  if (pusi & TS_UNIT_START) {
    i = f->data.ptr[f->data.out];
    if (i >= paylen) {
      warn (LWAR,"PSI pointer exceed",ETST,4,1,b);
      s->u.m.psi_length = 0;
      list_incr (f->data.out,f->data,paylen);
      return (TRUE);
    }
    if (s->u.m.psi_length == 0) {
      b = i + 1;
      complete = FALSE;
    } else {
      seclen = s->u.m.psi_length + i;
      b = 1;
      complete = TRUE;
    }
  } else {
    if (s->u.m.psi_length == 0) {
      f->total += TS_PACKET_SIZE;
      list_incr (f->data.out,f->data,paylen);
      return (TRUE);
    } else {
      b = 0;
      complete = FALSE;
    }
  }
  list_incr (f->data.out,f->data,b);
  b = paylen - b;
  if (s->u.m.psi_length + b > sizeof(s->u.m.psi_data)) {
    warn (LWAR,"PSI overflow",ETST,4,2,s->u.m.psi_length + b);
    s->u.m.psi_length = 0;
    list_incr (f->data.out,f->data,b);
    return (TRUE);
  }
  ts_adaption_field (f,adf,s,s);
  while (b > 0) {
    i = MAX_DATA_RAWB - f->data.out;
    if (i > b) {
      i = b;
    }
    memcpy (&s->u.m.psi_data[s->u.m.psi_length],
            &f->data.ptr[f->data.out],i);
    list_incr (f->data.out,f->data,i);
    s->u.m.psi_length += i;
    b -= i;
  }
  if (complete) {
    if (s->u.m.psi_length >= TS_HEADSLEN) {
      i = ((s->u.m.psi_data[TS_SECTIONLEN] & 0x0F) << 8)
        + s->u.m.psi_data[TS_SECTIONLEN+1] + TS_HEADSLEN;
    } else {
      i = 0;
    }
    if (seclen != i) {
      warn (LWAR,"PSI length wrong",ETST,4,i,seclen);
      s->u.m.psi_length -= seclen;
      memmove (&s->u.m.psi_data[0],&s->u.m.psi_data[seclen],s->u.m.psi_length);
      complete = FALSE;
    }
  }
  if (!complete) {
    if (s->u.m.psi_length >= TS_HEADSLEN) {
      seclen = ((s->u.m.psi_data[TS_SECTIONLEN] & 0x0F) << 8)
             + s->u.m.psi_data[TS_SECTIONLEN+1] + TS_HEADSLEN;
      if (s->u.m.psi_length >= seclen) {
        complete = TRUE;
      }
    }
  }
  while (complete) {
    if (tableid < 0) {
      if (marker_check (s->u.m.psi_data[TS_SECTIONLEN],0x00,0x40)) {
        warn (LWAR,"PSI section syntax ind",ETST,4,5,
            s->u.m.psi_data[TS_SECTIONLEN]);
      }
    } else if (s->u.m.psi_data[TS_TABLE_ID] == tableid) {
      if (marker_check (s->u.m.psi_data[TS_SECTIONLEN],0x80,0xC0)) {
        warn (LWAR,"PSI section syntax ind",ETST,4,6,
            s->u.m.psi_data[TS_SECTIONLEN]);
      } else {
        unsigned char a;
        a = s->u.m.psi_data[TS_VERSIONNB];
        if ((i = update_crc_32_block (CRC_INIT_32,
                &s->u.m.psi_data[0],seclen)) != 0) {
          warn (LWAR,"PSI CRC error",ETST,4,11,i);
        } else {
          i = (s->u.m.psi_data[TS_TRANSPORTID] << 8)
              + s->u.m.psi_data[TS_TRANSPORTID+1];
          switch (tableid) {
            case TS_TABLEID_PAT:
              if (seclen <= TS_MAX_SECTLEN) {
                eval_pat_section (f,s,seclen,i,(a >> 1) & 0x1F,a & 1,
                  s->u.m.psi_data[TS_SECTIONNB],s->u.m.psi_data[TS_LASTSECNB]);
              } else {
                warn (LWAR,"PSI data error",ETST,4,8,seclen);
              }
              break;
            case TS_TABLEID_CAT:
              if ((i == 0xFFFF)
               && (seclen <= TS_MAX_SECTLEN)) {
                eval_cat_section (f,s,seclen,(a >> 1) & 0x1F,a & 1,
                  s->u.m.psi_data[TS_SECTIONNB],s->u.m.psi_data[TS_LASTSECNB]);
              } else {
                warn (LWAR,"PSI data error",ETST,4,9,seclen);
              }
              break;
            case TS_TABLEID_PMT:
              if ((s->u.m.psi_data[TS_SECTIONNB] == 0)
               && (s->u.m.psi_data[TS_LASTSECNB] == 0)
               && (seclen <= TS_MAX_SECTLEN)
               && (seclen >= TS_PMTSECT_SIZE)) {
                eval_pmt_section (f,s,seclen,i,(a >> 1) & 0x1F,a & 1);
              } else {
                warn (LWAR,"PSI data error",ETST,4,10,seclen);
              }
              break;
          }
        }
      }
    } else {
      warn (LWAR,"PSI wrong table id",ETST,4,4,s->u.m.psi_data[TS_TABLE_ID]);
    }
    complete = FALSE;
    s->u.m.psi_length -= seclen;
    if (s->u.m.psi_length > 0) {
      if (s->u.m.psi_data[seclen] == TS_TABLEID_STUFFING) {
        s->u.m.psi_length = 0;
      } else {
        memmove (&s->u.m.psi_data[0],&s->u.m.psi_data[seclen],
            s->u.m.psi_length);
        if (s->u.m.psi_length >= TS_HEADSLEN) {
          seclen = ((s->u.m.psi_data[TS_SECTIONLEN] & 0x0F) << 8)
                 + s->u.m.psi_data[TS_SECTIONLEN+1] + TS_HEADSLEN;
          if (s->u.m.psi_length >= seclen) {
            complete = TRUE;
          }
        }
      }
    }
  }
  return (TRUE);
}

/* Check for a single tsauto entry, whether a given pes id matches
 * For each program in source PAT/PMT check and connect if applicable.
 * If none matches, but tsauto entry is sprg=unknown, check that sid.
 * Precondition: f!=NULL.
 * Return: TRUE, if at least one match, FALSE otherwise.
 */
static boolean split_autostreammatch (file_descr *f,
    int pid,
    byte sid,
    tsauto_descr *a)
{
  int i;
  boolean match, inpmt;
  pmt_descr *pmt;
  match = FALSE;
  inpmt = FALSE;
  pmt = f->u.ts.pat;
  while (pmt != NULL) {
    i = pmt->streams;
    while (--i >= 0) {
      if (pmt->stream[i] == pid) {
        warn (LINF,"Auto Streamtype",ETST,13,pmt->streamtype[i],pid);
        inpmt = TRUE;
        if (a == NULL) {
          warn (LIMP,"Auto T",ETST,13,sid,pmt->programnumber);
          ts_file_stream (f,pid) =
            connect_streamprog (f,pmt->programnumber,pid,
              -sid,
              guess_streamtype(sid,pmt->streamtype[i]),
              ts_file_stream (f,pid),
              ts_file_stream (f,pmt->pmt_pid),TRUE);
          match = TRUE;
          i = 0;
        } else {
          if (a->sprg > 0) {
            if (a->sprg == pmt->programnumber) {
              if (a->ssid < 0) {
                warn (LIMP,"Auto P",ETST,13,sid,a->tprg);
                ts_file_stream (f,pid) =
                  connect_streamprog (f,a->tprg,pid,
                    -sid,
                    guess_streamtype(sid,pmt->streamtype[i]),
                    ts_file_stream (f,pid),
                    ts_file_stream (f,pmt->pmt_pid),TRUE);
                match = TRUE;
                i = 0;
              } else if (a->ssid == sid) {
                warn (LIMP,"Auto S",ETST,13,a->tsid,a->tprg);
                ts_file_stream (f,pid) =
                  connect_streamprog (f,a->tprg,pid,
                    a->tsid,
                    guess_streamtype(a->tsid<0?-a->tsid:a->tsid,
                      pmt->streamtype[i]),
                    ts_file_stream (f,pid),
                    ts_file_stream (f,pmt->pmt_pid),TRUE);
                match = TRUE;
                i = 0;
              }
            }
          }
        }
      }
    }
    pmt = pmt->next;
  }
  if ((!inpmt) /* pid not registered in PAT/PMT -> check for unknown sprg */
   && (a != NULL)
   && (a->sprg == 0)
   && (a->ssid == sid)) {
    warn (LIMP,"Auto 0",ETST,9,a->tsid,a->tprg);
    ts_file_stream (f,pid) =
      connect_streamprog (f,a->tprg,pid,
        a->tsid,
        guess_streamtype(a->tsid<0?-a->tsid:a->tsid,-1),
        ts_file_stream (f,pid),
        ts_file_stream (f,0),TRUE);
    match = TRUE;
  }
  return (match);
}

/* For a packet that does not belong to an active stream, check whether
 * to open a stream for it automatically.
 * Precondition: f!=NULL.
 * Return: TRUE, if a stream was opened now, FALSE otherwise.
 */
static boolean split_autostream (file_descr *f,
    int pid)
{
  pmt_descr *pmt;
  boolean r;
  tsauto_descr *a;
  tsauto_descr **aa;
  warn (LDEB,"Auto Stream",ETST,9,0,pid);
  r = FALSE;
  if (pusi & TS_UNIT_START) {
    if ((f->automatic)
     || (f->u.ts.tsauto != NULL)) {
      if (paylen >= PES_HDCODE_SIZE) {
        int x;
        x = f->data.out;
        list_incr (x,f->data,TS_PACKET_SIZE - paylen);
        if (f->data.ptr[x] == 0x00) {
          list_incr (x,f->data,1);
          if (f->data.ptr[x] == 0x00) {
            list_incr (x,f->data,1);
            if (f->data.ptr[x] == 0x01) {
              list_incr (x,f->data,1);
              pmt = f->u.ts.pat;
              while (pmt != NULL) {
                if (pmt->pmt_pid == pid) {
                  warn (LWAR,"Auto PMT PID",ETST,9,r,pid);
                  return (r);
                }
                pmt = pmt->next;
              }
              if (f->automatic) {
                split_autostreammatch (f,pid,f->data.ptr[x],NULL);
                r = TRUE;
              }
              aa = &f->u.ts.tsauto;
              a = *aa;
              while (a != NULL) {
                if (split_autostreammatch (f,pid,f->data.ptr[x],a)) {
                  r = TRUE;
                  if (a->ssid >= 0) {
                    *aa = a->next; /* delete single entries when matched */
                    free (a);
                  } else {
                    aa = &((a)->next);
                  }
                } else {
                  aa = &((a)->next);
                }
                a = *aa;
              }
            }
          }
        }
      }
    }
  }
  return (r);
}

/* For a packet that does not belong to an active stream, check whether
 * it is categorized to be unparsed SI.
 * Precondition: f!=NULL, f->content==ct_transport.
 * Return: higher bound of range, if it is unparsed SI, -1 otherwise.
 */
int split_unparsedsi (file_descr *f,
    int pid)
{
  tssi_descr *tssi;
  tssi = f->u.ts.tssi;
  while (tssi != NULL) {
    warn (LDEB,"Split Chk SI",ETST,12,tssi->pid_low,tssi->pid_high);
    if ((pid >= tssi->pid_low)
     && (pid <= tssi->pid_high)) {
      warn (LDEB,"Split Chk SI",ETST,12,-1,pid);
      return (tssi->pid_high);
    }
    tssi = tssi->next;
  }
  warn (LDEB,"Split Chk SI",ETST,12,pid,-1);
  return (-1);
}

/* Extract one TS packet of not-to-be-parsed SI.
 * Precondition: f!=NULL, pid is unparsed_si,
 *               list_size(f->data) >= TS_PACKET_SIZE.
 * Return: TRUE if something was processed, FALSE if no data/space available
 */
static boolean ts_unparsed_si (file_descr *f)
{
  stream_descr *s;
  ctrl_buffer *c;
  s = ts_file_stream (f,TS_UNPARSED_SI);
  if (s != NULL) {
    if (!list_full (s->ctrl)) {
      c = &s->ctrl.ptr[s->ctrl.in];
      if (list_free (s->data) >= (2*TS_PACKET_SIZE-1)) {
        if (TS_PACKET_SIZE > list_freeinend (s->data)) {
          s->data.in = 0;
        }
        c->index = s->data.in;
        c->length = 0;
        while (c->length < TS_PACKET_SIZE) {
          int i;
          i = MAX_DATA_RAWB - f->data.out;
          if (i > (TS_PACKET_SIZE - c->length)) {
            i = TS_PACKET_SIZE - c->length;
          }
          memcpy (&s->data.ptr[s->data.in],&f->data.ptr[f->data.out],i);
          list_incr (s->data.in,s->data,i);
          list_incr (f->data.out,f->data,i);
          c->length += i;
        }
        f->payload += TS_PACKET_SIZE;
        c->sequence = f->sequence++;
        c->scramble = 0;
        c->msecread = msec_now ();
/* c->msecpush not set, because there is no scr/pcr or similar available */
/*
        c->pcr.valid = FALSE;
        c->opcr.valid = FALSE;
*/
        list_incr (s->ctrl.in,s->ctrl,1);
      } else {
        return (FALSE);
      }
    } else {
      return (FALSE);
    }
  } else {
    list_incr (f->data.out,f->data,TS_PACKET_SIZE);
  }
  f->total += TS_PACKET_SIZE;
  return (TRUE);
}

/* Check an otherwise unused stream for PCR.
 * Precondition: f!=NULL
 */
void split_checkpcrpid (file_descr *f,
    int pid)
{
  pmt_descr *pmt;
  pmt = f->u.ts.pat;
  while (pmt != NULL) {
    if (pmt->pcr_pid == pid) {
      ts_adaption_field (f,f->data.out,
          ts_file_stream (f,pmt->pmt_pid),ts_file_stream (f,pmt->pmt_pid));
      return;
/* only needed, if any of the streams is in use, but then the pcr
 * evaluation might come too late. therefor eval always.
 */
    }
    pmt = pmt->next;
  }
}

/* Split data from a TS stream.
 * Precondition: f!=NULL
 * Return: TRUE, if something was processed, FALSE if not enough data available
 */
boolean split_ts (file_descr *f)
{
  int l, pid;
  warn (LDEB,"Split TS",ETST,0,0,f);
  if (ts_skip_to_syncbyte (f)) {
    l = list_size (f->data);
    if (l >= TS_PACKET_SIZE) {
      pid = ts_packet_headinfo (&f->data);
      if ((pid >= TS_PID_LOWEST) && (pid <= TS_PID_HIGHEST)) {
        if (ts_file_stream (f,pid) != NULL) {
          if (ts_file_stream (f,pid)->streamdata == sd_data) {
            return (ts_data_stream (f,pid));
          } else {
            return (ts_psi_table_section (f,pid,TS_TABLEID_PMT));
          }
        } else {
          if (split_autostream (f,pid)) {
            return (ts_data_stream (f,pid));
          }
          if (split_unparsedsi (f,pid) >= 0) {
            warn (LDEB,"Unparsed SI",ETST,0,2,pid);
            return (ts_unparsed_si (f));
          }
          split_checkpcrpid (f,pid);
          warn (LDEB,"Data Packet (ignored)",ETST,0,1,pid);
          f->total += TS_PACKET_SIZE;
          list_incr (f->data.out,f->data,TS_PACKET_SIZE);
          return (TRUE);
        }
      } else if (pid == TS_PID_PAT) {
        return (ts_psi_table_section (f,TS_PID_PAT,TS_TABLEID_PAT));
/*
      } else if (pid == TS_PID_CAT) {
        return (ts_psi_table_section (f,TS_PID_CAT,TS_TABLEID_CAT));
*/
      } else if (pid == TS_PID_NULL) {
        f->total += TS_PACKET_SIZE;
        list_incr (f->data.out,f->data,TS_PACKET_SIZE);
        return (TRUE);
      } else if (split_unparsedsi (f,pid) >= 0) {
        warn (LDEB,"Unparsed SI",ETST,0,3,pid);
        return (ts_unparsed_si (f));
      } else {
        /* don't skip 188 here, because it might be an asynchronity */
        f->skipped += 1;
        f->total += 1;
        list_incr (f->data.out,f->data,1);
        return (TRUE);
      }
    }
  }
  return (FALSE);
}


LinuxTV legacy CVS <linuxtv.org/cvs>