File:  [DVB] / multiplexer / splicets.c
Revision 1.1: download - view: text, annotated - select for diffs
Mon Mar 19 20:52:34 2001 UTC (23 years, 2 months ago) by oskar
Branches: MAIN
CVS tags: HEAD
Multiplexer, first archived version.
New feature: --badtiming (see --help)

/*
 * ISO 13818 stream multiplexer
 * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin
 * Author: Oskar Schirmer (oskar@convergence.de)
 */

/*
 * Module:  Splice TS
 * Purpose: Generate transport stream.
 *
 * This module generates from the available input stream data (as
 * seperated by the split functions) the complete output stream.
 * It provides functions to handle programs for the resulting stream,
 * as these are output format dependent. Further, it accepts PSI data
 * just in time, validating it not earlier than with the arrival of
 * the corresponding payload at this stage.
 */

#include "global.h"
#include "crc.h"
#include "error.h"
#include "input.h"
#include "output.h"
#include "descref.h"
#include "pes.h"
#include "ts.h"
#include "splice.h"
#include "splicets.h"

const boolean splice_multipleprograms = TRUE;

static boolean changed_pat;
static int pat_section;
static const int last_patsection = 0;
static byte nextpat_version;
static byte pat_conticnt;

static int transportstreamid;

static int psi_size;
static int psi_done;
static byte psi_data [MAX_PSI_SIZE];

static byte unit_start;
static byte *conticnt;
static int psi_pid;

static int progs;
static prog_descr *prog [MAX_OUTPROG];

static int nextpid;
static stream_descr *outs [MAX_STRPERTS];

static int next_psi_periodic;
static int psi_frequency_msec;
static boolean psi_frequency_changed;

boolean splice_init (void)
{
  progs = 0;
  nextpid = 0;
  memset (outs,0,sizeof(outs));
  changed_pat = TRUE;
  pat_section = 0;
  nextpat_version = 0;
  pat_conticnt = 0;
  psi_size = psi_done = 0;
  unit_start = TS_UNIT_START;
  transportstreamid = 0x4227;
  psi_frequency_msec = 0;
  psi_frequency_changed = FALSE;
  return (TRUE);
}

void splice_settransportstreamid (int tsid)
{
  transportstreamid = tsid;
}

void splice_setpsifrequency (int freq)
{
  psi_frequency_msec = freq;
  psi_frequency_changed = TRUE;
}

static prog_descr *openprog (int programnb)
{
  int i;
  i = progs;
  while (--i >= 0) {
    if (prog[i]->program_number == programnb) {
      return (prog[i]);
    }
  }
  return (NULL);
}

static int findapid (stream_descr *s)
{
  boolean ok = TRUE;
  do {
    if ((nextpid < TS_PID_LOWEST) || (nextpid >= TS_PID_HIGHEST)) {
      if (!ok) {
        warn (LERR,"No PID found",ETSC,2,1,0);
        return (0);
      }
      ok = FALSE;
      nextpid = TS_PID_LOWEST;
    } else {
      nextpid += 1;
    }
  } while (outs[nextpid] != NULL);
  outs[nextpid] = s;
  warn (LDEB,"Next PID",ETSC,2,2,nextpid);
  return (nextpid);
}

prog_descr *splice_openprog (int programnb)
{
  prog_descr *p;
  int pid;
  warn (LIMP,"Open prog",ETSC,1,0,programnb);
  p = openprog (programnb);
  if (p == NULL) {
    if (progs < MAX_OUTPROG) {
      if ((pid = findapid (PMT_STREAM)) > 0) {
        if ((p = malloc(sizeof(prog_descr))) != NULL) {
          p->program_number = programnb;
          p->pcr_pid = -1;
          p->pmt_pid = pid;
          p->map_sequence = -1;
          p->pmt_conticnt = 0;
          p->pmt_version = 0;
          p->changed = TRUE;
          p->pat_section = 0; /* more ? */
          p->streams = 0;
          prog[progs++] = p;
          changed_pat = TRUE;
        } else {
          outs[pid] = NULL;
          warn (LERR,"Open prog",ETSC,1,1,0);
        }
      }
    } else {
      warn (LERR,"Max prog open",ETSC,1,2,0);
    }
  }
  return (p);
}

void splice_closeprog (prog_descr *p)
{
  int i, n;
  stream_descr *s;
  file_descr *f;
  warn (LIMP,"Close prog",ETSC,3,0,p->program_number);
  while (p->streams > 0) {
    s = p->stream[0];
    input_delprog (s,p);
    splice_delstream (p,s);
    if (s->u.d.progs == 0) {
      f = s->fdescr;
      input_closestream (s);
      input_closefileifunused (f);
    }
  }
  n = -1;
  if (p->pmt_pid >= 0) {
    i = progs;
    while (--i >= 0) {
      if (prog[i]->pmt_pid == p->pmt_pid) {
        n += 1;
      }
    }
  }
  i = progs;
  while (--i >= 0) {
    if (prog[i] == p) {
      prog[i] = prog[--progs];
      if (n == 0) {
        outs[p->pmt_pid] = NULL;
      }
      free (p);
      changed_pat = TRUE;
      return;
    }
  }
  warn (LERR,"Close lost prog",ETSC,3,1,progs);
}

boolean splice_emptyprog (prog_descr *p)
{
  return (p->streams == 0);
}

int splice_addstream (prog_descr *p,
    stream_descr *s,
    boolean force_sid)
{
  int pid = 0;
  warn (LIMP,"Add stream",ETSC,4,force_sid,s->stream_id);
  if (p->streams < MAX_STRPERPRG) {
    if ((pid = findapid (s)) > 0) {
      if (!force_sid) {
        s->stream_id = input_findfreestreamid (p,s->stream_id);
      }
      p->stream[p->streams++] = s;
      p->changed = TRUE;
      s->u.d.pid = pid;
    }
  }
  return (pid);
}

boolean splice_delstream (prog_descr *p,
    stream_descr *s)
{
  int i;
  warn (LIMP,"Del stream",ETSC,5,0,s->u.d.pid);
  i = p->streams;
  while (--i >= 0) {
    if (p->stream[i] == s) {
      outs[s->u.d.pid] = NULL;
      p->stream[i] = p->stream[--(p->streams)];
      p->changed = TRUE;
      if (p->pcr_pid == s->u.d.pid) {
        p->pcr_pid = -1;
      }
      s->u.d.pid = 0;
      return (TRUE);
    }
  }
  warn (LERR,"Del lost stream",ETSC,5,1,p->streams);
  return (FALSE);
}

static int make_patsection (int section,
    byte *dest)
{
  int i;
  byte *d;
  d = dest;
  *d++ = TS_TABLEID_PAT;
  d += 2;
  *d++ = transportstreamid >> 8;
  *d++ = (byte)transportstreamid;
  *d++ = 0xC0 | 0x01 | (nextpat_version << 1);
  *d++ = section;
  *d++ = last_patsection;
  i = progs;
  while (--i >= 0) {
    if (prog[i]->pat_section == section) {
      int x;
      x = prog[i]->program_number;
      *d++ = (x >> 8);
      *d++ = x;
      x = prog[i]->pmt_pid;
      *d++ = 0xE0 | (x >> 8);
      *d++ = x;
    }
  }
  i = d + CRC_SIZE - dest - TS_TRANSPORTID;
  dest[TS_SECTIONLEN] = 0xB0 | (i >> 8);
  dest[TS_SECTIONLEN+1] = i;
  crc32_calc (dest,i + TS_TRANSPORTID - CRC_SIZE,d);
  return (i + TS_TRANSPORTID);
}

static int make_pmtsection (stream_descr *s,
    prog_descr *p,
    byte *dest)
{
  int i;
  byte *d;
  p->changed = FALSE;
  d = dest;
  *d++ = TS_TABLEID_PMT;
  d += 2;
  i = p->program_number;
  *d++ = (i >> 8);
  *d++ = i;
  *d++ = 0xC0 | 0x01 | (p->pmt_version << 1);
  p->pmt_version = (p->pmt_version+1) & 0x1F;
  *d++ = 0;
  *d++ = 0;
  if (p->pcr_pid < 0) {
    stream_descr *pcrs;
    pcrs = input_findpcrstream (p);
    if (pcrs == NULL) {
      pcrs = s;
    }
    pcrs->u.d.has_clockref = TRUE;
    pcrs->u.d.next_clockref = msec_now () - MAX_MSEC_PCRDIST;
    p->pcr_pid = pcrs->u.d.pid;
  }
  i = p->pcr_pid;
  *d++ = 0xE0 | (i >> 8);
  *d++ = i;
  d += 2;
  i = NUMBER_ELEMD;
  while (--i > 0) {
    if (s->fdescr->content == ct_program) {
      byte *y;
      y = s->fdescr->u.ps.stream[0]->elemdvld[i]; /* this one ? why ? */
      if (y != NULL) {
        memcpy (d,y,y[1]+2);
        d += y[1]+2;
      }
    }
  }
  i = d - dest - (TS_PMT_PILEN+2);
  dest[TS_PMT_PILEN] = 0xF0 | (i >> 8);
  dest[TS_PMT_PILEN+1] = i;
  i = p->streams;
  while (--i >= 0) {
    if (p->stream[i]->u.d.mention) {
      int x;
      byte *e;
      *d++ = p->stream[i]->stream_type;
      x = p->stream[i]->u.d.pid;
      *d++ = 0xE0 | (x >> 8);
      *d++ = x;
      d += 2;
      e = d;
      x = NUMBER_ELEMD;
      while (--x > 0) {
        byte *y;
        y = p->stream[i]->elemdvld[x];
        if (y != NULL) {
          memcpy (d,y,y[1]+2);
          d += y[1]+2;
        }
      }
      x = d - e;
      *--e = x;
      *--e = 0xF0 | (x >> 8);
    }
  }
  i = d + CRC_SIZE - dest - TS_TRANSPORTID;
  dest[TS_SECTIONLEN] = 0xB0 | (i >> 8);
  dest[TS_SECTIONLEN+1] = i;
  crc32_calc (dest,i + TS_TRANSPORTID - CRC_SIZE,d);
  return (i + TS_TRANSPORTID);
}

void process_finish (void)
{
  warn (LIMP,"Finish",ETSC,6,0,0);
}

stream_descr *process_something (stream_descr *s)
{
  byte *d;
  int pid;
  byte scramble;
  ctrl_buffer *c;
  int l, f, k;
  int privdata;
  int adapt_ext_len;
  int now;
  byte adapt_field_ctrl;
  byte adapt_flags1, adapt_flags2;
  warn (LDEB,"Splice TS",ETSC,0,0,s->ctrl.out);
  if (s->isamap) {
    validate_mapref (s);
    return (NULL);
  }
  c = &s->ctrl.ptr[s->ctrl.out];
  if (psi_size > 0) {
    pid = psi_pid;
    scramble = 0;
    k = psi_size;
  } else {
    if (unit_start != 0) {
      now = msec_now ();
      if ((psi_frequency_changed)
       || ((psi_frequency_msec > 0)
        && ((next_psi_periodic - now) <= 0))) {
        changed_pat = TRUE;
        l = progs;
        while (--l >= 0) {
          prog[l]->changed = TRUE;
        }
        psi_frequency_changed = FALSE;
        next_psi_periodic = now + psi_frequency_msec;
      }
      if (changed_pat) {
        psi_pid = TS_PID_PAT;
        conticnt = &pat_conticnt;
        psi_data[0] = 0;
        psi_size = make_patsection (pat_section,&psi_data[1]) + 1;
        if (pat_section >= last_patsection) {
          changed_pat = FALSE;
          nextpat_version = (nextpat_version+1) & 0x1F;
          pat_section = 0;
        } else {
          pat_section += 1;
        }
        psi_done = 0;
        pid = psi_pid;
        scramble = 0;
        k = psi_size;
      } else {
        l = s->u.d.progs;
        while (--l >= 0) {
          if (s->u.d.pdescr[l]->changed) {
            f = s->u.d.pdescr[l]->streams;
            while ((--f >= 0)
                && (!s->u.d.pdescr[l]->stream[f]->u.d.mention)) {
            }
            if (f >= 0) {
              psi_pid = s->u.d.pdescr[l]->pmt_pid;
              conticnt = &s->u.d.pdescr[l]->pmt_conticnt;
              psi_data[0] = 0;
              psi_size = make_pmtsection (s,s->u.d.pdescr[l],&psi_data[1]) + 1;
              psi_done = 0;
              pid = psi_pid;
              scramble = 0;
              k = psi_size;
              break;
            }
          }
        }
        if (l < 0) {
          s->data.ptr[c->index+PES_STREAM_ID] = s->stream_id;
          conticnt = &s->conticnt;
          pid = s->u.d.pid;
          scramble = c->scramble;
          k = c->length;
        }
      }
    } else {
      pid = s->u.d.pid;
      scramble = c->scramble;
      k = c->length;
    }
  }
  d = output_pushdata (TS_PACKET_SIZE,c->time.push + s->u.d.delta);
  if (d == NULL) {
    return (s);
  }
  adapt_ext_len = 1;
  adapt_flags2 = 0;
  adapt_flags1 = 0;
  f = TS_PACKET_SIZE - TS_PACKET_HEADSIZE;
  if ((psi_size <= 0)
   && (s->u.d.discontinuity)) { /* o, not for contents, but PCR-disco ? */
    s->u.d.discontinuity = FALSE;
    adapt_flags1 |= TS_ADAPT_DISCONTI;
  }
  if (0) {
    adapt_flags1 |= TS_ADAPT_RANDOMAC;
  }
  if (0) {
    adapt_flags1 |= TS_ADAPT_PRIORITY;
  }
  if ((psi_size <= 0)
   && (s->u.d.has_clockref)
   && ((c->pcr.valid)
    || (s->u.d.next_clockref - (c->time.push + s->u.d.delta) <= 0))) {
/*
fprintf (stderr,"n=%10d, p=%10d, (now %10d)\n",
s->u.d.next_clockref,
c->time.push + s->delta,
msec_now ());
*/
    adapt_flags1 |= TS_ADAPT_PCRFLAG;
    f -= 6;
  }
  if ((psi_size <= 0)
   && (c->opcr.valid)) {
    adapt_flags1 |= TS_ADAPT_OPCRFLAG;
    f -= 6;
  }
  if (0) {
    adapt_flags1 |= TS_ADAPT_SPLICING;
    f -= 1;
  }
  if (0) {
    adapt_flags1 |= TS_ADAPT_TPRIVATE;
    privdata = 0;
    f -= (privdata + 1);
  }
  if (0) {
    adapt_flags2 |= TS_ADAPT_LTWFLAG;
    adapt_ext_len += 2;
  }
  if (0) {
    adapt_flags2 |= TS_ADAPT_PIECEWRF;
    adapt_ext_len += 3;
  }
  if (0) {
    adapt_flags2 |= TS_ADAPT_SEAMLESS;
    adapt_ext_len += 5;
  }
  if (adapt_flags2 != 0) {
    adapt_flags1 |= TS_ADAPT_EXTENSIO;
    f -= adapt_ext_len;
  }
  if (adapt_flags1 != 0) {
    f -= 2;
  }
  if (k < f) {
    l = k;
    if (adapt_flags1 == 0) {
      f -= 1;
      if (f > l) {
        f -= 1;
      }
    }
    adapt_field_ctrl = TS_AFC_BOTH;
  } else {
    l = f;
    if (f == 0) {
      adapt_field_ctrl = TS_AFC_ADAPT;
    } else if (adapt_flags1 == 0) {
      adapt_field_ctrl = TS_AFC_PAYLD;
    } else {
      adapt_field_ctrl = TS_AFC_BOTH;
    }
  }
  *d++ = TS_SYNC_BYTE;
  warn (LSEC,"Splice unitstart",ETSC,0,1,unit_start);
  warn (LSEC,"Splice PID",ETSC,0,2,pid);
  *d++ = (0 << 7) /* transport_error_indicator */
       | unit_start
       | (0 << 5) /* transport_priority */
       | (pid >> 8);
  *d++ = pid;
  *d++ = (scramble << 6)
       | adapt_field_ctrl
       | *conticnt;
  warn (LSEC,"Splice continuity cnt",ETSC,0,3,*conticnt);
  if (adapt_field_ctrl & TS_AFC_PAYLD) {
    *conticnt = (*conticnt+1) & 0x0F;
  }
  if (adapt_field_ctrl & TS_AFC_ADAPT) {
    if ((*d++ = (TS_PACKET_SIZE - TS_PACKET_FLAGS1) - l) != 0) {
      *d++ = adapt_flags1;
      if (adapt_flags1 & TS_ADAPT_PCRFLAG) {
        clockref pcr;
        msec2clockref (c->time.push + s->u.d.delta,pcr);
        *d++ = (pcr.base >> 25) | (pcr.ba33 << 7);
        *d++ = pcr.base >> 17;
        *d++ = pcr.base >> 9;
        *d++ = pcr.base >> 1;
        *d++ = (pcr.base << 7) | (pcr.ext >> 8) | 0x7E;
        *d++ = pcr.ext;
        s->u.d.next_clockref =
          (c->time.push + s->u.d.delta) + MAX_MSEC_PCRDIST * 8/10;
        c->pcr.valid = FALSE;
      }
      if (adapt_flags1 & TS_ADAPT_OPCRFLAG) {
        *d++ = (c->opcr.base >> 25) | (c->opcr.ba33 << 7);
        *d++ = c->opcr.base >> 17;
        *d++ = c->opcr.base >> 9;
        *d++ = c->opcr.base >> 1;
        *d++ = (c->opcr.base << 7) | (c->opcr.ext >> 8) | 0x7E;
        *d++ = c->opcr.ext;
        c->opcr.valid = FALSE;
      }
      if (adapt_flags1 & TS_ADAPT_SPLICING) {
      }
      if (adapt_flags1 & TS_ADAPT_TPRIVATE) {
      }
      if (adapt_flags1 & TS_ADAPT_EXTENSIO) {
        *d++ = adapt_ext_len;
        *d++ = adapt_flags2 | 0x1F;
        if (adapt_flags2 & TS_ADAPT_LTWFLAG) {
        }
        if (adapt_flags2 & TS_ADAPT_PIECEWRF) {
        }
        if (adapt_flags2 & TS_ADAPT_SEAMLESS) {
        }
      }
    }
    if (f > l) {
      warn (LSEC,"Splice padding",ETSC,0,4,f-l);
      memset (d,-1,f-l);
      d += (f-l);
    }
  }
  if (l > 0) {
    if (psi_size > 0) {
      memcpy (d,&psi_data[psi_done],l);
      if (l < psi_size) {
        warn (LSEC,"Splice PSI Data",ETSC,0,s->stream_id,l);
        psi_done += l;
        psi_size -= l;
        unit_start = 0;
      } else {
        warn (LINF,"Splice PSI Done",ETSC,0,s->stream_id,l);
        psi_done = psi_size = 0;
        unit_start = TS_UNIT_START;
      }
    } else {
      memcpy (d,&s->data.ptr[c->index],l);
      if (l < c->length) {
        warn (LSEC,"Splice Data",ETSC,0,s->stream_id,l);
        c->length -= l;
        s->data.out = (c->index += l);
        unit_start = 0;
      } else {
        warn (LINF,"Splice Done",ETSC,0,s->stream_id,l);
        list_incr (s->ctrl.out,s->ctrl,1);
        if (list_empty (s->ctrl)) {
          s->data.out = s->data.in;
        } else {
          s->data.out = s->ctrl.ptr[s->ctrl.out].index;
        }
        s = NULL;
        unit_start = TS_UNIT_START;
      }
    }
  }
  return (s);
}


LinuxTV legacy CVS <linuxtv.org/cvs>