File:  [DVB] / multiplexer / repeatts.c
Revision 1.13: download - view: text, annotated - select for diffs
Fri Jul 14 17:59:53 2006 UTC (17 years, 10 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, HEAD
version 1.1.0

/*
 * ISO 13818 stream multiplexer / additional repeater tool
 * 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:  Repeater
 * Purpose: Additional tool to repeat and pipe an input stream.
 *
 * This tool accepts a (time1,time2,filename) tuple via stdin,
 * opens the filename, assuming it contains a ISO 13818 Transport Stream,
 * and sends it packet-wise to stdout timed in such a way, that
 * all pakets are sent equally distributed within time2 msec.
 * As long as no further tuple is provided, after time1 msec the same
 * file is sent over and over again.
 */


#include <stdio.h>
#include "global.h"

#define D(x) /* x */

#define MAX_ANOTATE (16 * 256)

static boolean quit;
static int cmdf, outf, nextf;

static int combln, comlln;
static byte combuf[MAX_DATA_COMB];
static int pollc;

static int dati, dato;
static byte data[MAX_ANOTATE];

static t_msec nextrdelay, nextfdelay;
static off_t nextpackets;

t_msec msec_now (void)
{
#define MSEC_EXPONENT 21
  static long last;
  static int local_delta;
  struct timeval tv;
  gettimeofday (&tv,NULL);
  if ((tv.tv_sec & (~((1L << MSEC_EXPONENT) - 1))) != last) {
    last = tv.tv_sec & (~((1L << MSEC_EXPONENT) - 1));
    local_delta += 1000 * (1L << MSEC_EXPONENT);
  }
  return ((tv.tv_sec & ((1L << MSEC_EXPONENT) - 1)) * 1000
         + tv.tv_usec / 1000 + local_delta);
}

static void command_help (char *command, char *errmsg)
{
  fprintf (stderr, "%s\nUsage:\t%s [OPTIONS...] [<file>]\n"
  "  -d <delay>\ttime in msec after which the sending shall be repeated\n"
  "  -t <time>\ttime in msec until the file shall be sent completely\n"
  "  -i\t\taccept from stdin tripels: <delay> <time> <file>\n\n"
  "Send <file> repeated every <delay> msec evenly distributed within <time> msec.\n"
  "When omitted, <time> defaults to <delay>. When <delay> is omitted, the file is\n"
  "sent only once, and <time> must be given. When <file> is ommitted, only -i must\n"
  "be given, to allow commands be enter through stdin.\n",
     errmsg, command);
}

static boolean line_complete (char **s1,
    char **s2,
    char **s3)
{
  int i;
  boolean b;
  *s3 = NULL;
  *s2 = NULL;
  *s1 = NULL;
  i = 0;
  while (i < combln) {
    if (combuf[i] == '\n') {
      comlln = i;
      while (i >= 0) {
        if (combuf[i] <= ' ') {
          combuf[i] = 0;
          b = TRUE;
        } else {
          if (b) {
            *s3 = *s2;
            *s2 = *s1;
            b = FALSE;
          }
          *s1 = &combuf[i];
        }
        i -= 1;
      }
      return (TRUE);
    }
    i += 1;
  }
  return (FALSE);
}

static boolean is_long (char *s,
    long *r)
{
  long i;
  char *e;
  if (s == NULL) {
    return (FALSE);
  }
  errno = 0;
  i = strtol (s,&e,0);
  if ((errno != 0)
   || (*e != 0)) {
    return (FALSE);
  }
  *r = i;
  return (TRUE);
}

static boolean command_do (char *arg1,
    char *arg2,
    char *arg3)
{
  long l1, l2;
  struct stat stat;
D(fprintf(stderr,"command_do(%s,%s,%s)\n",arg1,arg2,arg3));
  if (arg1 != NULL) {
    if (is_long (arg1, &l1)) {
      if (l1 < 0) {
        quit = TRUE;
        return (TRUE);
      }
      if (arg2 != NULL) {
        if (is_long (arg2, &l2)) {
          if (l2 >= 0) {
            if ((l1 >= l2) || (l1 == 0)) {
              if (arg3 != NULL) {
                if (nextf >= 0) {
                  close (nextf);
                }
                if ((nextf = open (arg3, O_RDONLY)) >= 0) {
                  if (fstat (nextf, &stat) == 0) {
  D(fprintf(stderr,"file %d, mode %07o, name %s, ino %ld, size %ld\n",nextf,stat.st_mode,arg3,stat.st_ino,stat.st_size));
                    if (S_ISREG (stat.st_mode)) {
                      if ((stat.st_size % TS_PACKET_SIZE) == 0) {
                        nextrdelay = l2;
                        nextpackets = stat.st_size / TS_PACKET_SIZE;
                        nextfdelay = l1;
  D(fprintf(stderr,"next opened(%d,%d,%d)\n",nextfdelay,nextrdelay,nextpackets));
                        return (TRUE);
                      } else {
                        fprintf (stderr, "File size not multiple of 188\n");
                      }
                    } else {
                      fprintf (stderr, "File not regular\n");
                    }
                  } else {
                    fprintf (stderr, "Cannot stat file\n");
                  }
                  close (nextf);
                  nextf = -1;
                } else {
                  fprintf (stderr, "Cannot open file\n");
                }
              } else {
                fprintf (stderr, "File name missing\n");
              }
            } else {
              fprintf (stderr, "0<delay<time not allowed\n");
            }
          } else {
            fprintf (stderr, "Time must not be negative\n");
          }
        } else {
          fprintf (stderr, "Time must be numeric\n");
        }
      } else {
        fprintf (stderr, "Time missing\n");
      }
    } else {
      fprintf (stderr, "Delay must be numeric\n");
    }
  } else {
    return (TRUE);
  }
  return (FALSE);
}

static boolean command_init (int cargc,
    char **cargv)
{
  char *cdelay = NULL;
  char *ctime = NULL;
  char *cfile = NULL;
  int cc = 0;
  nextf = -1;
  quit = FALSE;
  combln = 0;
  dati = dato = 0;
  pollc = -1;
  while (++cc < cargc) {
    if ((!strcmp(cargv[cc],"--help")) || (!strcmp(cargv[cc],"-h"))) {
      command_help (cargv[0],"");
      return (FALSE);
    } else if ((!strcmp(cargv[cc],"--version")) || (!strcmp(cargv[cc],"-V"))) {
      fprintf(stderr, MPLEX_VERSION "\n");
      return FALSE;
    } else if (!strcmp (cargv[cc],"-i")) {
      pollc = 0;
    } else if (!strcmp (cargv[cc],"-d")) {
      if ((cdelay != NULL) || (++cc >= cargc)) {
        command_help (cargv[0],"must not use -d twice.\n");
        return (FALSE);
      }
      cdelay = cargv[cc];
    } else if (!strcmp (cargv[cc],"-t")) {
      if ((ctime != NULL) || (++cc >= cargc)) {
        command_help (cargv[0],"must not use -t twice.\n");
        return (FALSE);
      }
      ctime = cargv[cc];
    } else {
      if (cfile != NULL) {
        command_help (cargv[0],"too many parameters.\n");
        return (FALSE);
      }
      cfile = cargv[cc];
    }
  }
  if (cfile != NULL) {
    if (!command_do (cdelay ? cdelay : "0", ctime ? ctime : cdelay, cfile)) {
      command_help (cargv[0],"");
      return (FALSE);
    }
  } else if ((ctime != NULL) || (cdelay != NULL) || (pollc < 0)) {
D(fprintf(stderr,"ctime=%p, cdelay=%p, pollc=%d\n", ctime, cdelay, pollc));
    command_help (cargv[0],"only -i must be given when started with no file.\n");
    return (FALSE);
  }
  cmdf = STDIN_FILENO;
  outf = STDOUT_FILENO;
  return ((cmdf >= 0) && (outf >= 0));
}

int main (int argc,
    char *argv[])
{
  int polli, pollo, polls;
  int toberead;
  int currentf;
  boolean dotime;
  t_msec rtime, ftime, rdelay, fdelay, now;
  struct pollfd ufds [3];
  off_t rpackets, rpartial, rpdone;
  if (command_init (argc,&argv[0])) {
    currentf = -1;
    rtime = ftime = msec_now ();
    while (!quit) {
      now = msec_now ();
D(fprintf(stderr,"now(%d)\n",now));
      if (currentf < 0) {
        toberead = 0;
        if (nextpackets > 0) {
          rpackets = nextpackets;
          rdelay = nextrdelay / nextpackets;
          rpartial = nextrdelay % nextpackets;
        } else {
          rpackets = 1;
          rdelay = 0;
          rpartial = 0;
        }
        rpdone = 0;
        fdelay = nextfdelay;
        if ((ftime-now) < 0) {
          ftime = now;
        }
        rtime = ftime;
        currentf = nextf;
        nextf = -1;
D(fprintf(stderr,"next current(%d,%d,%d)\n",currentf,fdelay,rdelay));
      }
      if (currentf >= 0) {
        if ((rtime - now) <= 0) {
          if ((((dato-dati-1) & (MAX_ANOTATE-1)) - toberead) > TS_PACKET_SIZE) {
            toberead += TS_PACKET_SIZE;
            rtime += rdelay;
            rpdone += rpartial;
            if (rpdone >= rpackets) {
              rpdone -= rpackets; /* equaly distribute the rounded msecs by */
              rtime += 1;      /* counting them in an rpackets modulo-space */
            }
            dotime = TRUE;
D(fprintf(stderr,"timer a(%d,%d,%d)\n",toberead,rtime,rpdone));
          } else {
            rtime = now;
            dotime = FALSE;
D(fprintf(stderr,"timer b(%d,%d)\n",toberead,rtime));
          }
        } else {
          dotime = TRUE;
D(fprintf(stderr,"timer c(%d,%d)\n",toberead,rtime));
        }
      } else {
        dotime = FALSE;
D(fprintf(stderr,"timer c(%d,%d)\n",toberead,rtime));
      }
      polls = pollc+1;
      if (pollc >= 0) {
        ufds[pollc].fd = cmdf;
        ufds[pollc].events = POLLIN;
      }
      if (dati != dato) {
        pollo = polls++;
        ufds[pollo].fd = outf;
        ufds[pollo].events = POLLOUT;
      } else {
        pollo = -1;
      }
      if (toberead > 0) {
        polli = polls++;
        ufds[polli].fd = currentf;
        ufds[polli].events = POLLIN;
      } else {
        polli = -1;
      }
      poll (&ufds[0], polls, dotime ? ((rtime-now) > 0) ? (rtime-now) : 0 : -1);
      if ((pollc >= 0)
       && (ufds[pollc].revents & POLLIN)) {
        char *s1, *s2, *s3;
        if (combln >= MAX_DATA_COMB-HIGHWATER_COM) {
          combln -= HIGHWATER_COM;
          memmove (&combuf[0], &combuf[HIGHWATER_COM], combln);
        }
        combln += read (cmdf, &combuf[combln], MAX_DATA_COMB-combln);
        while (line_complete (&s1, &s2, &s3)) {
          command_do (s1, s2, s3);
          combln -= comlln;
          memmove (&combuf[0], &combuf[comlln], combln);
        }
      }
      if ((polli >= 0)
       && (ufds[polli].revents & (POLLIN | POLLHUP | POLLERR))) {
        int l;
        if (ufds[polli].revents & POLLIN) {
          l = toberead;
          if (l > (MAX_ANOTATE - dati)) {
            l = MAX_ANOTATE - dati;
          }
          l = read (currentf, &data[dati], l);
          dati = (dati+l) & (MAX_ANOTATE-1);
          toberead -= l;
        } else {
          l = 0;
        }
        if (l == 0) {
          if ((nextf >= 0)
           || (fdelay == 0)) {
            close (currentf);
            currentf = -1;
          } else {
            lseek (currentf,0,SEEK_SET);
            toberead = ((toberead-1) / TS_PACKET_SIZE) * TS_PACKET_SIZE;
          }
          ftime += fdelay;
          now = msec_now ();
          if ((ftime-now) < 0) {
            ftime = now;
          }
          rtime = ftime;
        }
      }
      if ((pollo >= 0)
       && (ufds[pollo].revents & (POLLOUT | POLLHUP | POLLERR))) {
        if (ufds[pollo].revents & POLLOUT) {
          int l;
          if (dati < dato) {
            l = MAX_ANOTATE - dato;
          } else {
            l = dati - dato;
          }
          l = write (outf, &data[dato], l);
          dato = (dato+l) & (MAX_ANOTATE-1);
          if (l == 0) {
            quit = TRUE;
          }
        } else {
          quit = TRUE;
        }
      }
    }
    return (EXIT_SUCCESS);
  }
  return (EXIT_FAILURE);
}

LinuxTV legacy CVS <linuxtv.org/cvs>