File:  [DVB] / multiplexer / splitps.c
Revision 1.8: 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 PS
 * Purpose: Split a program stream.
 *
 * This module examines a program stream and copies the packets to
 * the input stream buffers. PSI data is extracted, descriptors are
 * copied to the mapstream (stream 0).
 */

#include "global.h"
#include "error.h"
#include "pes.h"
#include "ps.h"
#include "input.h"
#include "splice.h"
#include "descref.h"
#include "splitpes.h"
#include "splitps.h"

static int ps_program_end_code (file_descr *f)
{
  warn (LIMP,"Program End Code",EPST,3,0,0);
  /* close file, do ... */
  return (PES_HDCODE_SIZE);
}

static int ps_pack_header (file_descr *f,
    int l)
{
  int i, s;
  long x;
  byte a, b;
  clockref oldscr; /* used for correcting bad DVB scr values */
  if (l < PS_PACKHD_SIZE) {
    warn (LDEB,"Pack header (incomplete)",EPST,2,1,l);
    return (0);
  }
  i = f->data.out;
  list_incr (i,f->data,PS_PACKHD_STUFLN);
  s = (f->data.ptr[i] & 0x07) + PS_PACKHD_SIZE;
  if (l < s) {
    warn (LDEB,"Pack header (incomplete)",EPST,2,2,l);
    return (0);
  }
  oldscr = f->u.ps.ph.scr;
  warn (LINF,"Pack header",EPST,2,0,s);
  i = f->data.out;
  list_incr (i,f->data,PS_PACKHD_SCR);
  a = f->data.ptr[i];
  marker_check (a,0x44,0xC4);
  f->u.ps.ph.scr.ba33 = (a >> 5) & 1;
  x = ((a & 0x18) | ((a & 0x03) << 1)) << 7;
  list_incr (i,f->data,1);
  x = (x | f->data.ptr[i]) << 8;
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  marker_bit (a,2);
  x = (x | ((a & 0xF8) | ((a & 0x03) << 1))) << 7;
  list_incr (i,f->data,1);
  x = (x | f->data.ptr[i]);
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  marker_bit (a,2);
  f->u.ps.ph.scr.base = (x << 5) | (a >> 3);
  warn (LSEC,"SCR base",EPST,2,3,f->u.ps.ph.scr.base);
  if (accept_weird_scr
   && ((oldscr.base - (90*40+1)) == f->u.ps.ph.scr.base)) {
    /* the DVB card produces weird scr-s, every second scr is less than
       previous one, indicating an odd value decrease of 40ms. weird! */
    f->u.ps.ph.scr.base = oldscr.base + (90*40);
  }
  list_incr (i,f->data,1);
  b = f->data.ptr[i];
  marker_bit (b,0);
  f->u.ps.ph.scr.ext = ((a & 0x03) << 7) | (b >> 1);
  warn (LSEC,"SCR ext",EPST,2,4,f->u.ps.ph.scr.ext);
  f->u.ps.ph.scr.valid = TRUE;
  cref2msec (&f->u.ps.stream[0]->u.m.conv,
      f->u.ps.ph.scr,
      &f->u.ps.stream[0]->u.m.msectime);
  warn (LDEB,"(map time)",EPST,2,5,f->u.ps.stream[0]->u.m.msectime);
  list_incr (i,f->data,1);
  x = f->data.ptr[i] << 8;
  list_incr (i,f->data,1);
  x = (x | f->data.ptr[i]) << 6;
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  marker_check (a,0x03,0x03);
  f->u.ps.ph.muxrate = x | (a >> 2);
  warn (LSEC,"muxrate",EPST,2,6,f->u.ps.ph.muxrate);
  return (s);
}

static boolean ps_system_header (file_descr *f,
    int size)
{
  int i;
  byte a, sid;
  long x;
  boolean bbs;
  int bsb;
  warn (LINF,"System header",EPST,1,0,size);
  if (size < PS_SYSTHD_STREAM) {
    warn (LWAR,"System header too short",EPST,1,1,size);
    return (FALSE);
  }
  i = f->data.out;
  list_incr (i,f->data,PES_HEADER_SIZE);
  a = f->data.ptr[i];
  marker_bit (a,7);
  x = (a & 0x7F) << 8;
  list_incr (i,f->data,1);
  x = (x | f->data.ptr[i]) << 7;
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  marker_bit (a,0);
  f->u.ps.sh.ratebound = x | (a >> 1);
  warn (LSEC,"rate_bound",EPST,1,5,f->u.ps.sh.ratebound);
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  f->u.ps.sh.csps_flag = a & 0x01;
  warn (LSEC,"csps_flag",EPST,1,6,f->u.ps.sh.csps_flag);
  f->u.ps.sh.fixed_flag = (a >>= 1) & 0x01;
  warn (LSEC,"fixed_flag",EPST,1,7,f->u.ps.sh.fixed_flag);
  f->u.ps.sh.audiobound = a >> 1;
  warn (LSEC,"audiobound",EPST,1,8,f->u.ps.sh.audiobound);
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  marker_bit (a,5);
  f->u.ps.sh.videobound = a & 0x1F;
  warn (LSEC,"videobound",EPST,1,9,f->u.ps.sh.videobound);
  f->u.ps.sh.system_video_lock_flag = (a >>= 6) & 0x01;
  warn (LSEC,
    "system_video_lock_flag",EPST,1,10,f->u.ps.sh.system_video_lock_flag);
  f->u.ps.sh.system_audio_lock_flag = a >> 1;
  warn (LSEC,
    "system_audio_lock_flag",EPST,1,11,f->u.ps.sh.system_audio_lock_flag);
  list_incr (i,f->data,1);
  f->u.ps.sh.packet_rate_restriction_flag = f->data.ptr[i] >> 7;
  warn (LSEC,"packet_rate_restriction_flag",
    EPST,1,12,f->u.ps.sh.packet_rate_restriction_flag);
  memset (f->u.ps.sh.buffer_bound,0,sizeof(f->u.ps.sh.buffer_bound));
  size -= PS_SYSTHD_STREAM;
  while ((size -= PS_SYSTHD_STRLEN) >= 0) {
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    if (a & 0x80) {
      sid = a;
      warn (LSEC,"stream_id",EPST,1,13,sid);
      list_incr (i,f->data,1);
      a = f->data.ptr[i];
      if (marker_check (a,0xC0,0xC0)) {
        warn (LWAR,"Missing 11",EPST,1,3,a);
        return (FALSE);
      }
      bbs = (a >> 5) & 0x01;
      warn (LSEC,"buffer bound scale",EPST,1,14,bbs);
      list_incr (i,f->data,1);
      bsb = ((a & 0x1F) << 8) | f->data.ptr[i];
      warn (LSEC,"buffer size bound",EPST,1,15,bsb);
      /* register stream here, if auto */
      if (a == PES_JOKER_AUDIO) {
        a = PES_CODE_AUDIO;
        x = PES_NUMB_AUDIO;
      } else if (a == PES_JOKER_VIDEO) {
        a = PES_CODE_VIDEO;
        x = PES_NUMB_VIDEO;
      } else if (a >= PES_LOWEST_SID) {
        x = 1;
      } else {
        x = 0;
      }
      if (bbs) {
        bsb = -bsb;
      }
      while (--x >= 0) {
        f->u.ps.sh.buffer_bound[a-PES_LOWEST_SID] = bsb;
        a += 1;
      }
    } else {
      warn (LWAR,"Next bit 0",EPST,1,2,a);
      return (FALSE);
    }
  }
  if (size != -PS_SYSTHD_STRLEN) {
    warn (LWAR,"System header length",EPST,1,4,size);
    return (FALSE);
  }
  return (TRUE);
}

static boolean ps_stream_map (file_descr *f,
    int size)
{
  int i, psmv, psil, esil, esml;
  byte a, styp, esid;
  boolean cni;
  warn (LINF,"Stream Map",EPST,4,0,size);
  if ((size > (1018 + PES_HEADER_SIZE))
   || (size < PS_STRMAP_SIZE)) {
    warn (LWAR,"Map size bad",EPST,4,12,size);
    return (FALSE);
  }
  i = f->data.out;
  list_incr (i,f->data,PES_HEADER_SIZE);
  a = f->data.ptr[i];
  psmv = a & 0x1F;
  warn (LSEC,"Map Version",EPST,4,1,psmv);
  alloc_descriptor (f->u.ps.stream[0],0,0,psmv);
  cni = a >> 7;
  warn (LSEC,"Current Next",EPST,4,2,cni);
  list_incr (i,f->data,2);
  psil = f->data.ptr[i] << 8;
  list_incr (i,f->data,1);
  psil = psil | f->data.ptr[i];
  list_incr (i,f->data,1);
  warn (LSEC,"PS Info Length",EPST,4,3,psil);
  size -= PS_STRMAP_SIZE;
  if (size < psil) {
    warn (LWAR,"Invalid Size",EPST,4,7,size);
    return (FALSE);
  }
  size -= psil;
  while (psil > 0) {
    i = put_descriptor (f,f->u.ps.stream[0],i,&psil);
  }
  if (psil < 0) {
    warn (LWAR,"PS Info Broken",EPST,4,4,psil);
    return (FALSE);
  }
  if (cni) {
    finish_descriptor (f->u.ps.stream[0]);
  }
  esml = f->data.ptr[i] << 8;
  list_incr (i,f->data,1);
  esml = esml | f->data.ptr[i];
  list_incr (i,f->data,1);
  warn (LSEC,"ES Map Length",EPST,4,5,esml);
  if (size != esml) {
    warn (LWAR,"Invalid Size",EPST,4,8,size);
    return (FALSE);
  }
  while (esml > 0) {
    styp = f->data.ptr[i];
    list_incr (i,f->data,1);
    esid = f->data.ptr[i];
    list_incr (i,f->data,1);
    esil = f->data.ptr[i] << 8;
    list_incr (i,f->data,1);
    esil = esil | f->data.ptr[i];
    list_incr (i,f->data,1);
    warn (LSEC,"Stream Type",EPST,4,9,styp);
    warn (LSEC,"E Stream Id",EPST,4,10,esid);
    warn (LSEC,"ES Info Length",EPST,4,11,esil);
    if (esid >= PES_LOWEST_SID) {
      if (f->automatic) {
        if (f->u.ps.stream[esid] == NULL) {
          f->u.ps.stream[esid] =
            connect_streamprog (
                f,f->auto_programnb,esid,-esid,styp,
                NULL,f->u.ps.stream[0],FALSE);
        }
      }
    }
    esml -= (esil + 4);
    if (esml >= 0) {
      alloc_descriptor (f->u.ps.stream[0],esid,0,psmv);
      while (esil > 0) {
        i = put_descriptor (f,f->u.ps.stream[0],i,&esil);
      }
      if (esil < 0) {
        warn (LWAR,"ES Map Broken",EPST,4,13,esil);
        return (FALSE);
      }
      if (cni) {
        finish_descriptor (f->u.ps.stream[0]);
      }
    }
  }
  if (esml < 0) {
    warn (LWAR,"ES Map Broken",EPST,4,6,esml);
    return (FALSE);
  }
  return (TRUE);
}

/* Parse a 45 bit stream directory offset value in 3 parts a 15 bit.
 * Precondition: f!=NULL, result!=NULL.
 * Postcontition: *result = offset.
 * Return: Index increased by 6.
 */
static int ps_stream_dir_get45 (file_descr *f,
    int i,
    long long *result)
{
  byte a;
  int b, n;
  long long r;
  n = 2;
  r = 0;
  do {
    list_incr (i,f->data,1);
    b = f->data.ptr[i];
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    marker_bit (a,0);
    b = (b << 7) | (a >> 1);
    r = (r << 15) | b;
  } while (--n >= 0);
  *result = r;
  return (i);
}

static boolean ps_stream_directory (file_descr *f,
    int size)
{
  int i, n;
  long x;
  byte a;
  int numoau;
  long long prevdo, nextdo;
  warn (LINF,"Stream Dir",EPST,5,0,0);
  i = f->data.out;
  list_incr (i,f->data,PES_HEADER_SIZE);
  x = f->data.ptr[i];
  list_incr (i,f->data,1);
  a = f->data.ptr[i];
  marker_bit (a,0);
  numoau = (x << 7) | (a >> 1);
  warn (LSEC,"Num Acces Units",EPST,5,1,numoau);
  if (size != (PS_STRDIR_SIZE + numoau * PS_STRDIR_SIZEAU)) {
    warn (LWAR,"Invalid Size",EPST,5,2,size);
    return (FALSE);
  }
  i = ps_stream_dir_get45 (f,i,&prevdo);
  warn (LSEC,"Prev Dir Offset",EPST,5,3,((long)prevdo));
  i = ps_stream_dir_get45 (f,i,&nextdo);
  warn (LSEC,"Next Dir Offset",EPST,5,4,((long)nextdo));
  n = 0;
  while (n < numoau) {
    byte psid;
    boolean idi;
    int refo, btr, cpi;
    long long headpo;
    clockref pts; /* and process all this ... */
    list_incr (i,f->data,1);
    psid = f->data.ptr[i];
    warn (LSEC,"Packet Str Id",EPST,5,5,psid);
    i = ps_stream_dir_get45 (f,i,&headpo);
    if (headpo & (1LL << 44)) {
      headpo = (1LL << 44) - headpo;
    }
    warn (LSEC,"Head Pos Offset",EPST,5,6,((long)headpo));
    list_incr (i,f->data,1);
    refo = f->data.ptr[i];
    list_incr (i,f->data,1);
    refo = (refo << 8) | f->data.ptr[i];
    warn (LSEC,"Reference Offset",EPST,5,7,refo);
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    marker_check (a,0x81,0x81);
    pts.ba33 = (a >> 3) & 1;
    x = a & 0x06;
    list_incr (i,f->data,1);
    x = (x << 7) | f->data.ptr[i];
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    marker_bit (a,0);
    x = (x << 8) | (a & 0xFE);
    list_incr (i,f->data,1);
    x = (x << 7) | f->data.ptr[i];
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    marker_bit (a,0);
    pts.base = (x << 7) | (a >> 1);
    pts.ext = 0;
    warn (LSEC,"PTS base",EPST,5,8,pts.base);
    list_incr (i,f->data,1);
    btr = f->data.ptr[i];
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    marker_bit (a,0);
    btr = (btr << 8) | (a & 0xFE);
    list_incr (i,f->data,1);
    btr = (btr << 7) | f->data.ptr[i];
    list_incr (i,f->data,1);
    a = f->data.ptr[i];
    marker_bit (a,7);
    cpi = (a >> 4) & 0x03;
    idi = (a >> 6) & 0x01;
    n += 1;
  }
  return (TRUE);
}

static boolean ps_data_stream (file_descr *f,
    int size,
    byte sourceid)
{
  stream_descr *s;
  ctrl_buffer *c;
  warn (LINF,"Data Stream",EPST,6,0,size);
  if ((f->u.ps.stream[sourceid] == NULL)
   && (f->automatic)) {
    f->u.ps.stream[sourceid] =
      connect_streamprog (f,f->auto_programnb,sourceid,-sourceid,
          guess_streamtype(sourceid,-1),NULL,f->u.ps.stream[0],FALSE);
  }
  s = f->u.ps.stream[sourceid];
  if (s != NULL) {
    if ((!list_full (s->ctrl))
     && (list_free (s->data) >= 2*size-1)) {
      c = &s->ctrl.ptr[s->ctrl.in];
      c->length = size;
      f->payload += size;
      f->total += size;
      c->index = pes_transfer (&f->data,&s->data,size);
      warn (LDEB,"Sequence",EPST,6,1,f->sequence);
      c->sequence = f->sequence++;
      c->scramble = 0;
      c->msecread = msec_now ();
      c->msecpush = f->u.ps.stream[0]->u.m.msectime;
      c->pcr.valid = FALSE;
      c->opcr.valid = FALSE;
      list_incr (s->ctrl.in,s->ctrl,1);
      return (TRUE);
    }
    return (FALSE);
  }
  f->total += size;
  list_incr (f->data.out,f->data,size);
  return (TRUE);
}

/* Split data from a PS stream.
 * Precondition: f!=NULL
 * Return: TRUE, if something was processed, FALSE if no data/space available
 */
boolean split_ps (file_descr *f)
{
  int l, p;
  byte a;
  warn (LDEB,"Split PS",EPST,0,0,f);
  if (pes_skip_to_prefix (f)) {
    l = list_size (f->data);
    if (l >= PES_HDCODE_SIZE) {
      a = pes_stream_id (&f->data);
      if (a >= PS_CODE_SYST_HDR) {
        if (l >= PES_HEADER_SIZE) {
          p = pes_packet_length (&f->data);
          p += PES_HEADER_SIZE;
          if (l >= p) {
            switch (a) {
              case PS_CODE_SYST_HDR:
                if (!ps_system_header (f,p)) {
                  p = PES_SYNC_SIZE;
                }
                break;
              case PES_CODE_STR_MAP:
                if (!ps_stream_map (f,p)) {
                  p = PES_SYNC_SIZE;
                }
                break;
              case PES_CODE_PADDING:
                break;
              case PES_CODE_PRIVATE2:
/*                p = PES_SYNC_SIZE; */
                break;
              case PES_CODE_ECM:
/*                p = PES_SYNC_SIZE; */
                break;
              case PES_CODE_EMM:
/*                p = PES_SYNC_SIZE; */
                break;
              case PES_CODE_DSMCC:
/*                p = PES_SYNC_SIZE; */
                break;
              case PES_CODE_ITU222E:
/*                p = PES_SYNC_SIZE; */
                break;
              case PES_CODE_STR_DIR:
                if (!ps_stream_directory (f,p)) {
                  p = PES_SYNC_SIZE;
                }
                break;
              default:
                return (ps_data_stream (f,p,a));
                break;
            }
          } else {
            p = 0;
          }
        } else {
          p = 0;
        }
      } else if (a == PS_CODE_END) {
        p = ps_program_end_code (f);
      } else if (a == PS_CODE_PACK_HDR) {
        p = ps_pack_header (f,l);
      } else {
        warn (LWAR,"Unknown Stream Id",EPST,0,1,a);
        p = PES_SYNC_SIZE;
      }
      if (p > 0) {
        f->total += p;
        list_incr (f->data.out,f->data,p);
        return (TRUE);
      }
    }
  }
  return (FALSE);
}


LinuxTV legacy CVS <linuxtv.org/cvs>