File:  [DVB] / multiplexer / spliceps.c
Revision 1.16: download - view: text, annotated - select for diffs
Sat Jan 15 20:39:50 2005 UTC (19 years, 4 months ago) by oskar
Branches: MAIN
CVS tags: version-1-1-7, version-1-1-6, version-1-1-5, version-1-1-4, version-1-1-3, version-1-1-2, version-1-1-1, version-1-1-0, version-1-0-8, version-1-0-7, version-1-0-6, HEAD
add command --nit to control network pid in pat

/*
 * ISO 13818 stream multiplexer
 * Copyright (C) 2001 Convergence Integrated Media GmbH Berlin
 * Copyright (C) 2005 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:  Splice PS
 * Purpose: Generate a program 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 (though multiple programs are
 * obsolete for a program stream). 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 "crc32.h"
#include "error.h"
#include "input.h"
#include "output.h"
#include "descref.h"
#include "pes.h"
#include "ps.h"
#include "splice.h"
#include "spliceps.h"

const boolean splice_multipleprograms = FALSE;

static int psi_size;
static byte psi_data [MAX_PSI_SIZE + PS_SYSTHD_SIZE + PS_STRMAP_SIZE +
    MAX_STRPERPRG * (PS_SYSTHD_STRLEN + PS_STRMAP_STRLEN)];

static prog_descr prog;

boolean splice_specific_init (void)
{
  prog.program_number = 0;
  prog.pmt_conticnt = 0;
  prog.pmt_version = 0;
  prog.changed = TRUE;
  prog.streams = 0;
  prog.stump = NULL;
  clear_descrdescr (&prog.manudescr);
  psi_size = 0;
  return (TRUE);
}

void splice_settransportstreamid (int tsid)
{
}

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

void splice_setnetworkpid (short pid)
{
}

void splice_all_configuration (void)
{
  if (configuration_must_print) {
    fprintf (stderr, configuration_total, 1);
    splice_one_configuration (&prog);
    configuration_was_printed;
  }
}

void splice_addsirange (file_descr *f,
    int lower,
    int upper)
{
}

void splice_createstump (int programnb,
    short pid,
    byte styp)
{
}
 
stump_descr *splice_getstumps (int programnb,
    short pid)
{
  return (NULL);
}

void splice_modifytargetdescriptor (int programnb,
    short sid,
    short pid,
    int dtag,
    int dlength,
    byte *data)
{
  splice_modifytargetdescrprog (&prog,0,sid,pid,dtag,dlength,data,NULL);
}

prog_descr *splice_getprog (int programnb)
{
  return (&prog);
}

prog_descr *splice_openprog (int programnb)
{
/*  prog.program_number = programnb;  must stay zero */
/*  configuration_changed = TRUE; */
  warn (LIMP,"Open prog",EPSC,1,0,programnb);
  return (&prog);
}

void splice_closeprog (prog_descr *p)
{
  stream_descr *s;
  file_descr *f;
  warn (LIMP,"Close prog",EPSC,2,0,prog.streams);
  while (prog.streams > 0) {
    s = prog.stream[0];
    input_delprog (s,&prog);
    splice_delstream (&prog,s);
    if (s->u.d.progs == 0) {
      f = s->fdescr;
      input_closestream (s);
      input_closefileifunused (f);
    } else {
      warn (LERR,"Close prog",EPSC,2,s->u.d.progs,s->stream_id);
    }
  }
  configuration_changed = TRUE;
}

int splice_addstream (prog_descr *p,
    stream_descr *s,
    boolean force_sid)
{
  /* add stream to main program */
  int sid = 0;
  warn (LIMP,"Add stream",EPSC,3,force_sid,s->stream_id);
  if (prog.streams < MAX_STRPERPRG) {
    if (!force_sid) {
      s->stream_id = splice_findfreestreamid (&prog,s->stream_id);
    }
    prog.stream[prog.streams++] = s;
    prog.changed = TRUE;
    s->u.d.pid = sid = s->stream_id;
    configuration_changed = TRUE;
    splice_modifycheckmatch (0,&prog,s,NULL);
  }
  return (sid);
}

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

void process_finish (void)
{
  byte *d;
  warn (LIMP,"Finish",EPSC,5,0,0);
  d = output_pushdata (PES_HDCODE_SIZE, FALSE, 0);
  if (d != NULL) {
    *d++ = 0x00;
    *d++ = 0x00;
    *d++ = 0x01;
    *d++ = PS_CODE_END;
    warn (LIMP,"Finish Done",EPSC,5,1,0);
  }
}

static int make_systemheader (stream_descr *s,
    byte *dest)
{
  int i, pid;
  byte v, a;
  byte *d;
  d = dest;
  *d++ = 0x00;
  *d++ = 0x00;
  *d++ = 0x01;
  *d++ = PS_CODE_SYST_HDR;
  d += 2;
  if (s->fdescr->content == ct_program) {
    i = s->fdescr->u.ps.sh.ratebound;
  } else {
    i = 4500000 / 400; /* wrong for sure */
  }
  *d++ = 0x80
       | (i >> 15);
  *d++ = (i >> 7);
  *d++ = (i << 1)
       | 0x01;
  d += 2;
  *d++ = 0 /* packet_rate_restriction_flag */
       | 0x7F;
  a = v = 0;
  i = prog.streams;
  while (--i >= 0) {
    pid = prog.stream[i]->u.d.pid;
    if ((pid >= PES_CODE_VIDEO)
     && (pid < (PES_CODE_VIDEO + PES_NUMB_VIDEO))) {
      if (v == 0) {
        *d++ = PES_JOKER_AUDIO;
        *d++ = 0xC0
             | (0 << 5) /* buffer bound scale */
             | ((4096/128) >> 8);
        *d++ = (4096/128) & 0xFF; /* fixme! propagate from input, or derive */
      }
      v += 1;
    } else {
      if ((pid >= PES_CODE_AUDIO)
       && (pid < (PES_CODE_AUDIO + PES_NUMB_AUDIO))) {
        if (a == 0) {
          *d++ = PES_JOKER_VIDEO;
          *d++ = 0xC0
               | (0 << 5) /* buffer bound scale */
               | ((237568/128) >> 8);
          *d++ = (237568/128) & 0xFF;
        }
        a += 1;
      } else {
        *d++ = pid;
        *d++ = 0xC0
             | (0 << 5) /* buffer bound scale */
             | ((2048/128) >> 8);
        *d++ = (2048/128) & 0xFF;
      }
    }
  }
  dest[PS_SYSTHD_AUDBND] = (a << 2)
       | 0 /* fixed_flag */
       | 0; /* CSPS_flag */
  dest[PS_SYSTHD_VIDBND] = 0 /* system_audio_lock_flag */
       | 0 /* system_video_lock_flag */
       | 0x20
       | v;
  i = d - dest - PES_HEADER_SIZE;
  dest[PES_PACKET_LENGTH] = (i >> 8);
  dest[PES_PACKET_LENGTH+1] = i;
  return (i + PES_HEADER_SIZE);
}

static int make_streammap (stream_descr *s,
    byte *dest)
{
  int i, l;
  byte *d;
  stream_descr *t;
  d = dest;
  *d++ = 0x00;
  *d++ = 0x00;
  *d++ = 0x01;
  *d++ = PES_CODE_STR_MAP;
  d += 2;
  *d++ = (1 << 7) /* current applicable */
       | 0x60
       | prog.pmt_version;
  *d++ = 0xFE
       | 0x01;
  d += 2;
  i = NUMBER_DESCR;
  while (--i >= 0) {
    byte *y;
    y = prog.manudescr.refx[i];
    if ((y == NULL)
     && (s->u.d.mapstream != NULL)) {
      y = s->u.d.mapstream->autodescr->refx[i];
    }
    if (y != NULL) {
      int yl = y[1];
      if (yl != 0) {
        yl += 2;
        memcpy (d,y,yl);
        d += yl;
      }
    }
  }
  l = d - dest - PS_STRMAP_PSID;
  dest[PS_STRMAP_PSIL] = (l >> 8);
  dest[PS_STRMAP_PSIL+1] = l;
  d += 2;
  i = prog.streams;
  while (--i >= 0) {
    t = prog.stream[i];
    if (t->u.d.mention) {
      int x;
      byte *e;
      *d++ = t->stream_type;
      *d++ = t->u.d.pid;
      d += 2;
      e = d;
      x = NUMBER_DESCR;
      while (--x >= 0) {
        byte *y;
        y = t->manudescr->refx[x];
        if (y == NULL) {
          y = t->autodescr->refx[x];
        }
        if (y != NULL) {
          int yl = y[1];
          if (yl != 0) {
            yl += 2;
            memcpy (d,y,yl);
            d += yl;
          }
        }
      }
      x = d - e;
      *--e = x;
      *--e = (x >> 8);
    }
  }
  i = d - dest - l - (PS_STRMAP_SIZE - CRC_SIZE);
  dest[PS_STRMAP_PSID + l] = (i >> 8);
  dest[PS_STRMAP_PSID+l+1] = i;
  i = d + CRC_SIZE - dest - PES_HEADER_SIZE;
  dest[PES_PACKET_LENGTH] = (i >> 8);
  dest[PES_PACKET_LENGTH+1] = i;
  crc32_calc (dest,i + PES_HEADER_SIZE - CRC_SIZE,d);
  return (i + PES_HEADER_SIZE);
}

stream_descr *process_something (stream_descr *s)
{
  byte *d;
  ctrl_buffer *c;
  clockref pcr;
  int i;
  t_msec now;
  warn (LDEB,"Splice PS",EPSC,0,0,s->ctrl.out);
  if (s->streamdata == sd_map) {
    validate_mapref (s);
    return (NULL);
  }
  c = &s->ctrl.ptr[s->ctrl.out];
  now = msec_now ();
  if ((psi_frequency_changed)
   || ((psi_frequency_msec > 0)
    && ((next_psi_periodic - now) <= 0))) {
    prog.unchanged = TRUE;
    psi_frequency_changed = FALSE;
    next_psi_periodic = now + psi_frequency_msec;
  }
  if (prog.unchanged || prog.changed) {
    if (prog.changed) {
      prog.pmt_version = (prog.pmt_version+1) & 0x1F;
    }
    psi_size = make_systemheader (s,&psi_data[0]);
    psi_size += make_streammap (s,&psi_data[psi_size]);
    prog.changed = FALSE;
    prog.unchanged = FALSE;
  }
  i = c->msecpush + s->u.d.delta;
  d = output_pushdata (PS_PACKHD_SIZE + psi_size + c->length, TRUE, i);
  if (d == NULL) {
    return (s);
  }
  *d++ = 0x00;
  *d++ = 0x00;
  *d++ = 0x01;
  *d++ = PS_CODE_PACK_HDR;
  msec2cref (&s->u.d.conv, i, &pcr);
  *d++ = 0x40
       | (((pcr.ba33 << 5) | (pcr.base >> 27)) & 0x38)
       | 0x04
       | ((pcr.base >> 28) & 0x03);
  *d++ = (pcr.base >> 20);
  *d++ = ((pcr.base >> 12) & 0xF8)
       | 0x04
       | ((pcr.base >> 13) & 0x03);
  *d++ = (pcr.base >> 5);
  *d++ = ((pcr.base << 3) & 0xF8)
       | 0x04
       | ((pcr.ext >> 7) & 0x03);
  *d++ = (pcr.ext << 1)
       | 0x01;
  if (s->fdescr->content == ct_program) {
    i = s->fdescr->u.ps.ph.muxrate; /* wrong, because maybe old */
  } else {
    i = 4500000 / 400; /* xxx wrong for sure */
  }
  *d++ = (i >> 14);
  *d++ = (i >> 6);
  *d++ = (i << 2)
       | 0x03;
  *d++ = 0xF8
       | 0; /* stuffing length */
  if (psi_size > 0) {
    memcpy (d,&psi_data[0],psi_size);
    d += psi_size;
    psi_size = 0;
  }
  memcpy (d,&s->data.ptr[c->index],c->length);
  d[PES_STREAM_ID] = s->stream_id;
  warn (LINF,"Splice Done",EPSC,0,s->stream_id,c->length);
  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;
  }
  return (NULL);
}


LinuxTV legacy CVS <linuxtv.org/cvs>